diff --git a/changelog/unreleased/enhancement-password-generator-for-public-links b/changelog/unreleased/enhancement-password-generator-for-public-links new file mode 100644 index 00000000000..627d5ee64e7 --- /dev/null +++ b/changelog/unreleased/enhancement-password-generator-for-public-links @@ -0,0 +1,9 @@ +Enhancement: Password generator for public links + +We've added a new button on the password input field for public links, +clicking on that button will fill the input with a generated password. + +If a password policy is set, those rules will also be applied. + +https://github.com/owncloud/web/pull/9691 +https://github.com/owncloud/web/issues/9666 diff --git a/packages/design-system/src/components/OcModal/OcModal.vue b/packages/design-system/src/components/OcModal/OcModal.vue index b89ed676cb5..ddef2dc2bb5 100644 --- a/packages/design-system/src/components/OcModal/OcModal.vue +++ b/packages/design-system/src/components/OcModal/OcModal.vue @@ -44,6 +44,7 @@ :label="inputLabel" :type="inputType" :password-policy="inputPasswordPolicy" + :generate-password-method="inputGeneratePasswordMethod" :description-message="inputDescription" :disabled="inputDisabled" :fix-message-line="true" @@ -371,6 +372,14 @@ export default defineComponent({ required: false, default: () => ({}) }, + /** + * Method to generate random password for the input + */ + inputGeneratePasswordMethod: { + type: Function as PropType<(...args: unknown[]) => string>, + required: false, + default: null + }, /** * Overwrite default focused element * Can be `#id, .class`. diff --git a/packages/design-system/src/components/OcTextInput/OcTextInput.spec.ts b/packages/design-system/src/components/OcTextInput/OcTextInput.spec.ts index c65c2b5b279..a1af8396070 100644 --- a/packages/design-system/src/components/OcTextInput/OcTextInput.spec.ts +++ b/packages/design-system/src/components/OcTextInput/OcTextInput.spec.ts @@ -56,7 +56,8 @@ describe('OcTextInput', () => { inputField: '.oc-text-input', infoIcon: '.oc-text-input-message .oc-icon', showPasswordToggleBtn: '.oc-text-input-show-password-toggle', - copyPasswordBtn: '.oc-text-input-copy-password-button' + copyPasswordBtn: '.oc-text-input-copy-password-button', + generatePasswordBtn: '.oc-text-input-generate-password-button' } describe('id prop', () => { @@ -121,6 +122,30 @@ describe('OcTextInput', () => { expect(wrapper.find(selectors.inputField).attributes().type).toBe('password') }) }) + describe('generate password button', () => { + it('should not exist if type is not "password" or prop "generatePasswordMethod" is not provided', () => { + const wrapper = getMountedWrapper() + expect(wrapper.find(selectors.generatePasswordBtn).exists()).toBeFalsy() + + const wrapper2 = getMountedWrapper({ props: { type: 'password' } }) + expect(wrapper2.find(selectors.generatePasswordBtn).exists()).toBeFalsy() + }) + it('should exist if type is "password" and prop "generatePasswordMethod" is provided', () => { + const wrapper = getMountedWrapper({ + props: { generatePasswordMethod: jest.fn(), type: 'password' } + }) + expect(wrapper.find(selectors.generatePasswordBtn).exists()).toBeTruthy() + }) + it('should fill input with generated password if clicked', async () => { + const wrapper = getMountedWrapper({ + props: { generatePasswordMethod: jest.fn(() => 'PAssword12#!'), type: 'password' } + }) + await wrapper.find(selectors.generatePasswordBtn).trigger('click') + expect((wrapper.find(selectors.inputField).element as HTMLInputElement).value).toEqual( + 'PAssword12#!' + ) + }) + }) describe('password policy', () => { it('should emit "passwordChallengeFailed" if password does not match criteria', async () => { const wrapper = getMountedWrapper( diff --git a/packages/design-system/src/components/OcTextInput/OcTextInput.vue b/packages/design-system/src/components/OcTextInput/OcTextInput.vue index bbb67cce3ea..e1e6897f900 100644 --- a/packages/design-system/src/components/OcTextInput/OcTextInput.vue +++ b/packages/design-system/src/components/OcTextInput/OcTextInput.vue @@ -232,6 +232,14 @@ export default defineComponent({ passwordPolicy: { type: Object as PropType, default: () => ({}) + }, + /** + * Method to generate random password + */ + generatePasswordMethod: { + type: Function as PropType<(...args: unknown[]) => string>, + required: false, + default: null } }, emits: [ @@ -264,6 +272,7 @@ export default defineComponent({ } if (this.type === 'password') { additionalAttrs['password-policy'] = this.passwordPolicy + additionalAttrs['generate-password-method'] = this.generatePasswordMethod } // Exclude listeners for events which are handled via methods in this component // eslint-disable-next-line no-unused-vars diff --git a/packages/design-system/src/components/OcTextInput/__snapshots__/OcTextInput.spec.ts.snap b/packages/design-system/src/components/OcTextInput/__snapshots__/OcTextInput.spec.ts.snap index 6060716133e..74a0684260c 100644 --- a/packages/design-system/src/components/OcTextInput/__snapshots__/OcTextInput.spec.ts.snap +++ b/packages/design-system/src/components/OcTextInput/__snapshots__/OcTextInput.spec.ts.snap @@ -2,25 +2,26 @@ exports[`OcTextInput password input field password policy displays error state if password does not match criteria 1`] = `
- +
- - - +
@@ -42,25 +43,26 @@ exports[`OcTextInput password input field password policy displays error state i exports[`OcTextInput password input field password policy displays success state if password matches criteria 1`] = `
- +
- - - +
diff --git a/packages/design-system/src/components/_OcTextInputPassword/_OcTextInputPassword.vue b/packages/design-system/src/components/_OcTextInputPassword/_OcTextInputPassword.vue index ded10248855..9a6c9f30a24 100644 --- a/packages/design-system/src/components/_OcTextInputPassword/_OcTextInputPassword.vue +++ b/packages/design-system/src/components/_OcTextInputPassword/_OcTextInputPassword.vue @@ -1,23 +1,40 @@