From 641600f2cd9d001c999fea8c89876a1972bb9fd7 Mon Sep 17 00:00:00 2001 From: CrowsVeldt Date: Thu, 16 Jan 2025 09:31:55 -0700 Subject: [PATCH 1/8] feat: add Accordion component, use in ChangeEmail and ChangePassword forms --- .../src/Accordion/Accordion.stories.tsx | 16 +++++++ .../src/Accordion/Accordion.test.tsx | 18 ++++++++ .../components/src/Accordion/Accordion.tsx | 44 +++++++++++++++++++ packages/components/src/index.ts | 1 + .../content/sections/ChangeEmail.form.tsx | 40 ++++------------- .../content/sections/ChangePassword.form.tsx | 32 +++----------- 6 files changed, 92 insertions(+), 59 deletions(-) create mode 100644 packages/components/src/Accordion/Accordion.stories.tsx create mode 100644 packages/components/src/Accordion/Accordion.test.tsx create mode 100644 packages/components/src/Accordion/Accordion.tsx diff --git a/packages/components/src/Accordion/Accordion.stories.tsx b/packages/components/src/Accordion/Accordion.stories.tsx new file mode 100644 index 0000000000..44f0cc4192 --- /dev/null +++ b/packages/components/src/Accordion/Accordion.stories.tsx @@ -0,0 +1,16 @@ +import { Text } from 'theme-ui' + +import { Accordion } from './Accordion' + +import type { Meta,StoryFn } from '@storybook/react' + +export default { + title: 'Components/Accordion', + component: Accordion, +} as Meta + +export const Default: StoryFn = () => ( + + Default + +) diff --git a/packages/components/src/Accordion/Accordion.test.tsx b/packages/components/src/Accordion/Accordion.test.tsx new file mode 100644 index 0000000000..f54cc9ba2b --- /dev/null +++ b/packages/components/src/Accordion/Accordion.test.tsx @@ -0,0 +1,18 @@ +import '@testing-library/jest-dom/vitest' + +import { describe, expect, it } from 'vitest' + +import { render } from '../test/utils' +import { Default } from './Accordion.stories' + +import type { IProps } from './Accordion' + +describe('Accordion', () => { + it('validates the component behaviour', () => { + const { getByText } = render( + , + ) + + expect(getByText('Accordion')).toBeInTheDocument(); + }) +}) diff --git a/packages/components/src/Accordion/Accordion.tsx b/packages/components/src/Accordion/Accordion.tsx new file mode 100644 index 0000000000..51c8843525 --- /dev/null +++ b/packages/components/src/Accordion/Accordion.tsx @@ -0,0 +1,44 @@ +import { useState } from 'react' +import { Flex, Heading, Text } from 'theme-ui' + +import { Icon } from '../Icon/Icon' + +import type { ThemeUIStyleObject } from 'theme-ui'; + +export interface IProps { + children: React.ReactNode + sx?: ThemeUIStyleObject | undefined + title: string + subtitle?: string +} + +export const Accordion = (props: IProps) => { + const [isExpanded, setIsExpanded] = useState(false) + const { children, sx, title, subtitle } = props + + return ( + + setIsExpanded(!isExpanded)} + > + + {title} + + + + + {subtitle != undefined && ( + {subtitle} + )} + {isExpanded && children} + + ) +} diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index d3dc87b09a..acb859df1f 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -76,3 +76,4 @@ export { VideoPlayer } from './VideoPlayer/VideoPlayer' export { CommentAvatar } from './CommentAvatar/CommentAvatar' export type { availableGlyphs } from './Icon/types' export type { ITab } from './SettingsFormWrapper/SettingsFormTab' +export { Accordion } from './Accordion/Accordion' diff --git a/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx b/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx index 972da5633f..b54e3ba883 100644 --- a/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx +++ b/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx @@ -1,12 +1,12 @@ import { useEffect, useState } from 'react' import { Field, Form } from 'react-final-form' -import { Button, FieldInput, Icon } from 'oa-components' +import { Button, FieldInput, Accordion } from 'oa-components' import { PasswordField } from 'src/common/Form/PasswordField' import { useCommonStores } from 'src/common/hooks/useCommonStores' import { FormFieldWrapper } from 'src/pages/common/FormFieldWrapper' import { UserContactError } from 'src/pages/User/contact/UserContactError' -import { buttons, fields, headings } from 'src/pages/UserSettings/labels' -import { Flex, Heading, Text } from 'theme-ui' +import { buttons, fields } from 'src/pages/UserSettings/labels' +import { Flex } from 'theme-ui' import type { SubmitResults } from 'src/pages/User/contact/UserContactError' @@ -16,13 +16,11 @@ interface IFormValues { } export const ChangeEmailForm = () => { - const [isExpanded, setIsExpanded] = useState(false) const [submitResults, setSubmitResults] = useState(null) const [currentEmail, setCurrentEmail] = useState(null) const { userStore } = useCommonStores().stores const formId = 'changeEmail' - const glyph = isExpanded ? 'arrow-full-up' : 'arrow-full-down' useEffect(() => { getUserEmail() @@ -36,7 +34,6 @@ export const ChangeEmailForm = () => { type: 'success', message: `Email changed to ${newEmail}. You've been sent two emails now(!) One to your old email address to check this was you and the other to your new address to verify it.`, }) - setIsExpanded(false) getUserEmail() } catch (error) { setSubmitResults({ type: 'error', message: error.message }) @@ -54,8 +51,10 @@ export const ChangeEmailForm = () => { sx={{ flexDirection: 'column', gap: 2 }} > - - {isExpanded && currentEmail && ( +
{ data-cy="changeEmailForm" sx={{ flexDirection: 'column', gap: 2 }} > - - {headings.changeEmail} - - - - {fields.email.title}: {currentEmail} - - { ) }} /> - )} - - + ) } diff --git a/src/pages/UserSettings/content/sections/ChangePassword.form.tsx b/src/pages/UserSettings/content/sections/ChangePassword.form.tsx index fa489a97c0..cdbd8a2972 100644 --- a/src/pages/UserSettings/content/sections/ChangePassword.form.tsx +++ b/src/pages/UserSettings/content/sections/ChangePassword.form.tsx @@ -1,12 +1,12 @@ import { useState } from 'react' import { Form } from 'react-final-form' -import { Button, FieldInput, Icon } from 'oa-components' +import { Accordion, Button, FieldInput } from 'oa-components' import { PasswordField } from 'src/common/Form/PasswordField' import { useCommonStores } from 'src/common/hooks/useCommonStores' import { FormFieldWrapper } from 'src/pages/common/FormFieldWrapper' import { UserContactError } from 'src/pages/User/contact/UserContactError' -import { buttons, fields, headings } from 'src/pages/UserSettings/labels' -import { Flex, Heading, Text } from 'theme-ui' +import { buttons, fields } from 'src/pages/UserSettings/labels' +import { Flex } from 'theme-ui' import type { SubmitResults } from 'src/pages/User/contact/UserContactError' @@ -17,12 +17,10 @@ interface IFormValues { } export const ChangePasswordForm = () => { - const [isExpanded, setIsExpanded] = useState(false) const [submitResults, setSubmitResults] = useState(null) const { userStore } = useCommonStores().stores const formId = 'changePassword' - const glyph = isExpanded ? 'arrow-full-up' : 'arrow-full-down' const onSubmit = async (values: IFormValues) => { const { oldPassword, newPassword } = values @@ -33,7 +31,6 @@ export const ChangePasswordForm = () => { type: 'success', message: `Password changed.`, }) - setIsExpanded(false) } catch (error) { setSubmitResults({ type: 'error', message: error.message }) } @@ -46,7 +43,7 @@ export const ChangePasswordForm = () => { > - {isExpanded && ( + { data-cy="changePasswordForm" sx={{ flexDirection: 'column', gap: 1 }} > - - {headings.changePassword} - - { ) }} /> - )} - - + ) } From c11d5e6b2fffe0266fa2d1e993bbe817d9dc0386 Mon Sep 17 00:00:00 2001 From: CrowsVeldt Date: Sat, 18 Jan 2025 14:18:33 -0700 Subject: [PATCH 2/8] feat: add cursor: pointer to Accordioncomponent --- packages/components/src/Accordion/Accordion.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/src/Accordion/Accordion.tsx b/packages/components/src/Accordion/Accordion.tsx index 51c8843525..297ecdb163 100644 --- a/packages/components/src/Accordion/Accordion.tsx +++ b/packages/components/src/Accordion/Accordion.tsx @@ -3,7 +3,7 @@ import { Flex, Heading, Text } from 'theme-ui' import { Icon } from '../Icon/Icon' -import type { ThemeUIStyleObject } from 'theme-ui'; +import type { ThemeUIStyleObject } from 'theme-ui' export interface IProps { children: React.ReactNode @@ -19,7 +19,7 @@ export const Accordion = (props: IProps) => { return ( Date: Sat, 18 Jan 2025 14:23:36 -0700 Subject: [PATCH 3/8] feat: update Accordion stories and test --- .../components/src/Accordion/Accordion.stories.tsx | 6 +++--- .../components/src/Accordion/Accordion.test.tsx | 13 +++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/components/src/Accordion/Accordion.stories.tsx b/packages/components/src/Accordion/Accordion.stories.tsx index 44f0cc4192..d8d1da8689 100644 --- a/packages/components/src/Accordion/Accordion.stories.tsx +++ b/packages/components/src/Accordion/Accordion.stories.tsx @@ -2,7 +2,7 @@ import { Text } from 'theme-ui' import { Accordion } from './Accordion' -import type { Meta,StoryFn } from '@storybook/react' +import type { Meta, StoryFn } from '@storybook/react' export default { title: 'Components/Accordion', @@ -10,7 +10,7 @@ export default { } as Meta export const Default: StoryFn = () => ( - - Default + + Now you see me! ) diff --git a/packages/components/src/Accordion/Accordion.test.tsx b/packages/components/src/Accordion/Accordion.test.tsx index f54cc9ba2b..b44dee4d7e 100644 --- a/packages/components/src/Accordion/Accordion.test.tsx +++ b/packages/components/src/Accordion/Accordion.test.tsx @@ -4,15 +4,16 @@ import { describe, expect, it } from 'vitest' import { render } from '../test/utils' import { Default } from './Accordion.stories' - import type { IProps } from './Accordion' describe('Accordion', () => { - it('validates the component behaviour', () => { - const { getByText } = render( - , - ) + it('displays the accordion body on click', () => { + const { getByText } = render() + const accordionTitle = getByText('Accordion Title') + expect(getByText('Now you see me!')).not.toBeInTheDocument() + + accordionTitle.click() - expect(getByText('Accordion')).toBeInTheDocument(); + expect(getByText('Now you see me!')).toBeInTheDocument() }) }) From bebd4e0c588af1095c7e77613d27b2f79dc8996a Mon Sep 17 00:00:00 2001 From: CrowsVeldt Date: Sat, 18 Jan 2025 14:24:15 -0700 Subject: [PATCH 4/8] fix: alphabatize index export --- packages/components/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index acb859df1f..9794dea0ec 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -1,3 +1,4 @@ +export { Accordion } from './Accordion/Accordion' export { ArticleCallToAction } from './ArticleCallToAction/ArticleCallToAction' export { Banner } from './Banner/Banner' export { BlockedRoute } from './BlockedRoute/BlockedRoute' @@ -76,4 +77,3 @@ export { VideoPlayer } from './VideoPlayer/VideoPlayer' export { CommentAvatar } from './CommentAvatar/CommentAvatar' export type { availableGlyphs } from './Icon/types' export type { ITab } from './SettingsFormWrapper/SettingsFormTab' -export { Accordion } from './Accordion/Accordion' From 14a07794958c51bf2f75382a2c5af0af7909a777 Mon Sep 17 00:00:00 2001 From: CrowsVeldt Date: Sat, 18 Jan 2025 14:24:50 -0700 Subject: [PATCH 5/8] feat: reorder email and password forms --- src/pages/UserSettings/SettingsPageAccount.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/UserSettings/SettingsPageAccount.tsx b/src/pages/UserSettings/SettingsPageAccount.tsx index 94efda4b1d..100949fea4 100644 --- a/src/pages/UserSettings/SettingsPageAccount.tsx +++ b/src/pages/UserSettings/SettingsPageAccount.tsx @@ -31,8 +31,8 @@ export const SettingsPageAccount = observer(() => { - + {title} From e0159aa2766d1995e3f3ba258a2c274f7d537ec0 Mon Sep 17 00:00:00 2001 From: CrowsVeldt Date: Sat, 18 Jan 2025 14:30:47 -0700 Subject: [PATCH 6/8] feat: add subtitle to change password form --- .../UserSettings/content/sections/ChangePassword.form.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/UserSettings/content/sections/ChangePassword.form.tsx b/src/pages/UserSettings/content/sections/ChangePassword.form.tsx index cdbd8a2972..1bbc2727b9 100644 --- a/src/pages/UserSettings/content/sections/ChangePassword.form.tsx +++ b/src/pages/UserSettings/content/sections/ChangePassword.form.tsx @@ -43,7 +43,10 @@ export const ChangePasswordForm = () => { > - + Date: Sat, 18 Jan 2025 14:33:09 -0700 Subject: [PATCH 7/8] fix: update failing spec for Accordion component --- packages/components/src/Accordion/Accordion.test.tsx | 4 +++- packages/components/src/Accordion/Accordion.tsx | 2 +- packages/components/src/index.ts | 4 ++-- packages/cypress/src/integration/SignUp.spec.ts | 4 ++-- src/pages/UserSettings/content/sections/ChangeEmail.form.tsx | 3 ++- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/components/src/Accordion/Accordion.test.tsx b/packages/components/src/Accordion/Accordion.test.tsx index b44dee4d7e..a5dd400f19 100644 --- a/packages/components/src/Accordion/Accordion.test.tsx +++ b/packages/components/src/Accordion/Accordion.test.tsx @@ -1,16 +1,18 @@ import '@testing-library/jest-dom/vitest' +import { screen } from '@testing-library/react' import { describe, expect, it } from 'vitest' import { render } from '../test/utils' import { Default } from './Accordion.stories' + import type { IProps } from './Accordion' describe('Accordion', () => { it('displays the accordion body on click', () => { const { getByText } = render() const accordionTitle = getByText('Accordion Title') - expect(getByText('Now you see me!')).not.toBeInTheDocument() + expect(screen.queryByText('Now you see me!')).not.toBeInTheDocument() accordionTitle.click() diff --git a/packages/components/src/Accordion/Accordion.tsx b/packages/components/src/Accordion/Accordion.tsx index 297ecdb163..7797c049ed 100644 --- a/packages/components/src/Accordion/Accordion.tsx +++ b/packages/components/src/Accordion/Accordion.tsx @@ -18,7 +18,7 @@ export const Accordion = (props: IProps) => { return ( { cy.get('[data-cy="tab-Account"]').click() cy.step('Update Email') - cy.get('[data-cy="changeEmailButton"]').click() + cy.get('[data-cy="accordionContainer"]').click({ multiple: true }) cy.get('[data-cy="changeEmailForm"]') .contains(`Current email address: ${email}`) .should('be.visible') @@ -92,7 +92,7 @@ describe('[User sign-up]', () => { .should('be.visible') cy.step('Update Password') - cy.get('[data-cy="changePasswordButton"]').click() + cy.get('[data-cy="accordionContainer"]').click({ multiple: true }) cy.get('[data-cy="oldPassword"]').clear().type(password) cy.get('[data-cy="newPassword"]').clear().type(newPassword) cy.get('[data-cy="repeatNewPassword"]').clear().type(newPassword) diff --git a/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx b/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx index b54e3ba883..e2041abe8d 100644 --- a/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx +++ b/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react' import { Field, Form } from 'react-final-form' -import { Button, FieldInput, Accordion } from 'oa-components' +import { Accordion, Button, FieldInput } from 'oa-components' import { PasswordField } from 'src/common/Form/PasswordField' import { useCommonStores } from 'src/common/hooks/useCommonStores' import { FormFieldWrapper } from 'src/pages/common/FormFieldWrapper' @@ -52,6 +52,7 @@ export const ChangeEmailForm = () => { > From ac06b9b594afe8a114d96025da4aca97c2435338 Mon Sep 17 00:00:00 2001 From: CrowsVeldt Date: Thu, 23 Jan 2025 08:37:30 -0700 Subject: [PATCH 8/8] feat: if accordion is closed clicking anywhere on it will open it --- packages/components/src/Accordion/Accordion.tsx | 5 +++++ packages/cypress/src/integration/SignUp.spec.ts | 2 +- src/pages/UserSettings/content/sections/ChangeEmail.form.tsx | 1 - 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/components/src/Accordion/Accordion.tsx b/packages/components/src/Accordion/Accordion.tsx index 7797c049ed..e7f0296fb8 100644 --- a/packages/components/src/Accordion/Accordion.tsx +++ b/packages/components/src/Accordion/Accordion.tsx @@ -20,6 +20,11 @@ export const Accordion = (props: IProps) => { { + if (!isExpanded) { + setIsExpanded(true) + } + }} > { cy.step('Update Email') cy.get('[data-cy="accordionContainer"]').click({ multiple: true }) - cy.get('[data-cy="changeEmailForm"]') + cy.get('[data-cy="changeEmailContainer"]') .contains(`Current email address: ${email}`) .should('be.visible') cy.get('[data-cy="newEmail"]').clear().type(newEmail) diff --git a/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx b/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx index e2041abe8d..10dede28e3 100644 --- a/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx +++ b/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx @@ -52,7 +52,6 @@ export const ChangeEmailForm = () => { >