diff --git a/packages/app/components/contact/ContactForm.tsx b/packages/app/components/contact/ContactForm.tsx index 364d5d0..f6d8e15 100644 --- a/packages/app/components/contact/ContactForm.tsx +++ b/packages/app/components/contact/ContactForm.tsx @@ -20,14 +20,14 @@ import { User } from 'app/components/user'; import styles from './contactForm.scss'; import messages from './contactForm.intl.json'; -const CONTACT_CATEGORIES = [ +const CONTACT_CATEGORIES = { // TODO: сюда позже проставить реальные id категорий с backend - , - , - , - , - , -]; + 0: , + 1: , + 2: , + 3: , + 4: , +}; export class ContactForm extends React.Component< { @@ -161,7 +161,12 @@ export class ContactForm extends React.Component<
-
); diff --git a/packages/app/components/footerMenu/FooterMenu.tsx b/packages/app/components/footerMenu/FooterMenu.tsx index f1b7d30..cd8d407 100644 --- a/packages/app/components/footerMenu/FooterMenu.tsx +++ b/packages/app/components/footerMenu/FooterMenu.tsx @@ -16,7 +16,7 @@ type Props = { class FooterMenu extends React.Component { render() { return ( -
+
diff --git a/packages/app/components/ui/form/Dropdown.js b/packages/app/components/ui/form/Dropdown.tsx similarity index 60% rename from packages/app/components/ui/form/Dropdown.js rename to packages/app/components/ui/form/Dropdown.tsx index e98291d..84944bd 100644 --- a/packages/app/components/ui/form/Dropdown.js +++ b/packages/app/components/ui/form/Dropdown.tsx @@ -1,42 +1,38 @@ -import PropTypes from 'prop-types'; -import React from 'react'; +import React, { InputHTMLAttributes } from 'react'; import ReactDOM from 'react-dom'; - +import { MessageDescriptor } from 'react-intl'; import clsx from 'clsx'; - -import { omit } from 'app/functions'; -import { colors, COLOR_GREEN } from 'app/components/ui'; +import { COLOR_GREEN, Color } from 'app/components/ui'; import styles from './dropdown.scss'; import FormInputComponent from './FormInputComponent'; -export default class Dropdown extends FormInputComponent { - static displayName = 'Dropdown'; +type I18nString = string | MessageDescriptor; +type ItemLabel = I18nString | React.ReactElement; - static propTypes = { - label: PropTypes.oneOfType([ - PropTypes.shape({ - id: PropTypes.string, - }), - PropTypes.string, - ]).isRequired, - items: PropTypes.arrayOf( - PropTypes.oneOfType([ - PropTypes.string, - PropTypes.shape({ - id: PropTypes.string, - }), - ]), - ).isRequired, - block: PropTypes.bool, - color: PropTypes.oneOf(colors), - }; +interface Props extends InputHTMLAttributes { + label: I18nString; + items: { [value: string]: ItemLabel }; + block?: boolean; + color: Color; +} - static defaultProps = { +interface OptionItem { + label: ItemLabel; + value: string; +} + +interface State { + isActive: boolean; + activeItem: OptionItem | null; +} + +export default class Dropdown extends FormInputComponent { + static defaultProps: Partial = { color: COLOR_GREEN, }; - state = { + state: State = { isActive: false, activeItem: null, }; @@ -52,12 +48,15 @@ export default class Dropdown extends FormInputComponent { } render() { - const { color, block, items } = this.props; + const { color, block, items, ...restProps } = this.props; const { isActive } = this.state; + delete restProps.label; + const activeItem = this.getActiveItem(); - const label = this.formatMessage(activeItem.label); - const props = omit(this.props, Object.keys(Dropdown.propTypes)); + const label = React.isValidElement(activeItem.label) + ? activeItem.label + : this.formatMessage(activeItem.label); return (
@@ -66,10 +65,13 @@ export default class Dropdown extends FormInputComponent { [styles.block]: block, [styles.opened]: isActive, })} - {...props} + data-e2e-select-name={restProps.name} + {...restProps} onClick={this.onToggle} > - {label} + + {label} +
@@ -96,7 +98,7 @@ export default class Dropdown extends FormInputComponent { }); } - onSelectItem(item) { + onSelectItem(item: OptionItem) { return event => { event.preventDefault(); @@ -106,9 +108,9 @@ export default class Dropdown extends FormInputComponent { }; } - getActiveItem() { + getActiveItem(): OptionItem { const { items } = this.props; - let { activeItem } = /** @type {any} */ (this.state); + let { activeItem } = this.state; if (!activeItem) { activeItem = { @@ -130,20 +132,20 @@ export default class Dropdown extends FormInputComponent { } getValue() { - return this.getActiveItem().value; + return this.getActiveItem()?.value; } - onToggle = event => { + onToggle = (event: React.MouseEvent) => { event.preventDefault(); this.toggle(); }; - onBodyClick = event => { + onBodyClick = (event: MouseEvent) => { if (this.state.isActive) { const el = ReactDOM.findDOMNode(this); - if (!el.contains(event.target) && el !== event.taget) { + if (!el.contains(event.target) && el !== event.target) { event.preventDefault(); event.stopPropagation(); diff --git a/packages/app/services/api/feedback.ts b/packages/app/services/api/feedback.ts index 9236484..a538c8d 100644 --- a/packages/app/services/api/feedback.ts +++ b/packages/app/services/api/feedback.ts @@ -2,6 +2,11 @@ import request from 'app/services/request'; export default { send({ subject = '', email = '', message = '', category = '' }) { - return request.post('/api/feedback', { subject, email, message, category }); + return request.post('/api/feedback', { + subject, + email, + message, + category, + }); }, }; diff --git a/tests-e2e/cypress/integration/profile/feedback-popup.test.ts b/tests-e2e/cypress/integration/profile/feedback-popup.test.ts new file mode 100644 index 0000000..1fc9f20 --- /dev/null +++ b/tests-e2e/cypress/integration/profile/feedback-popup.test.ts @@ -0,0 +1,51 @@ +import { account1 } from '../../fixtures/accounts.json'; + +it('should send feedback', () => { + const subject = 'Hello world'; + const message = 'This is a feedback message'; + + cy.server(); + cy.route({ + url: '/api/feedback', + method: 'POST', + response: { success: true }, + }); + + cy.login({ accounts: ['default'] }); + + cy.visit('/'); + + cy.getByTestId('footer') + .contains('Contact Us') + .click(); + cy.getByTestId('feedbackPopup').should('be.visible'); + + cy.get('[name=subject]').type(subject); + cy.get('[name=email]').should('have.value', account1.email); + cy.get('[name=message]').type(message); + + cy.get('[data-e2e-select-name=category]') + .getByTestId('select-label') + .should('contain', 'What are you interested'); + cy.get('[data-e2e-select-name=category]').click(); + cy.get('[data-e2e-select-name=category]') + .contains('bug') + .click(); + cy.get('[data-e2e-select-name=category]') + .getByTestId('select-label') + .should('contain', 'bug'); + + cy.getByTestId('feedbackPopup') + .get('[type=submit]') + .click(); + + cy.getByTestId('feedbackPopup').should( + 'contain', + 'Your message was received', + ); + cy.getByTestId('feedbackPopup').should('contain', account1.email); + + cy.getByTestId('feedback-popup-close-button').click(); + + cy.getByTestId('feedbackPopup').should('not.be.visible'); +});