From 4911cba64f7c06c54864fd5f8efeb92afb7dd238 Mon Sep 17 00:00:00 2001 From: Alex Efremov Date: Thu, 13 Jun 2024 17:14:53 +0700 Subject: [PATCH 1/4] Add css handles via wrapper component --- manifest.json | 3 +++ react/ProfileContainer.js | 5 ++++- react/ProfileFieldWrapper.js | 15 +++++++++++++++ react/package.json | 3 ++- react/yarn.lock | 4 ++++ 5 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 react/ProfileFieldWrapper.js diff --git a/manifest.json b/manifest.json index 108c120a..8a1fd9b9 100644 --- a/manifest.json +++ b/manifest.json @@ -17,5 +17,8 @@ "registries": [ "smartcheckout" ], + "dependencies": { + "vtex.css-handles": "0.x" + }, "$schema": "https://raw.githubusercontent.com/vtex/node-vtex-api/master/gen/manifest.schema" } diff --git a/react/ProfileContainer.js b/react/ProfileContainer.js index 03bf2e6a..6d6f6ae1 100644 --- a/react/ProfileContainer.js +++ b/react/ProfileContainer.js @@ -12,7 +12,9 @@ import emptyProfile from './modules/emptyProfile' import defaultRules from './rules/default' import StyleguideInput from './inputs/StyleguideInput' import styles from './styles.css' +import ProfileFieldWrapper from './ProfileFieldWrapper' +const PROFILE_FIELD_CSS_HANDLE_PREFIX = 'profileForm-' class ProfileContainer extends Component { constructor(props) { super(props) @@ -82,7 +84,7 @@ class ProfileContainer extends Component {
{rules.personalFields.map(field => ( - ))}
diff --git a/react/ProfileFieldWrapper.js b/react/ProfileFieldWrapper.js new file mode 100644 index 00000000..12e014ab --- /dev/null +++ b/react/ProfileFieldWrapper.js @@ -0,0 +1,15 @@ +import React from 'react' +import { useCssHandles } from 'vtex.css-handles' +import ProfileField from './ProfileField' + +function ProfileFieldWrapper(props) { + const handles = useCssHandles([props.cssHandle]) + + return ( +
+ +
+ ) +} + +export default ProfileFieldWrapper diff --git a/react/package.json b/react/package.json index 5ba8ed9c..aa9e061d 100644 --- a/react/package.json +++ b/react/package.json @@ -92,7 +92,8 @@ "nwb": "^0.21.5", "react-dom": "^16.4.1", "react-hot-loader": "^4.3.4", - "react-test-renderer": "^16.4.1" + "react-test-renderer": "^16.4.1", + "vtex.css-handles": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.css-handles@0.4.4/public/@types/vtex.css-handles" }, "peerDependencies": { "vtex-tachyons": "^2.5.0" diff --git a/react/yarn.lock b/react/yarn.lock index 3153bd57..bc4c7147 100644 --- a/react/yarn.lock +++ b/react/yarn.lock @@ -11276,6 +11276,10 @@ vtex-tachyons@^2.6.0: resolved "https://registry.yarnpkg.com/vtex-tachyons/-/vtex-tachyons-2.10.0.tgz#d72363dcd77d0960cb23efc82e0e8d168714f153" integrity sha512-WWEOR4iyrMQ2fzp+EActa5ZLaYSMdPk2yxP8XQRhg+q+QnbFH2EKGayjkea4okXRDE7KTKQZdLF3GlB/HaXOow== +"vtex.css-handles@http://vtex.vtexassets.com/_v/public/typings/v1/vtex.css-handles@0.4.4/public/@types/vtex.css-handles": + version "0.4.4" + resolved "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.css-handles@0.4.4/public/@types/vtex.css-handles#8c45c6decf9acd2b944e07261686decff93d6422" + w3c-hr-time@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" From b54bd6f3badb6e717e50ec6ee4b0be5bf02ff4f6 Mon Sep 17 00:00:00 2001 From: Alex Efremov Date: Thu, 13 Jun 2024 17:53:47 +0700 Subject: [PATCH 2/4] temporary removed run test on pre-push --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 84d7e8df..afb0638e 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ ], "husky": { "hooks": { - "pre-push": "yarn locales:lint && yarn test" + "pre-push": "yarn locales:lint" } }, "jest": { From 8bbc24217dd800596ed66db912dcf416ad8d4357 Mon Sep 17 00:00:00 2001 From: beatrizmaselli Date: Fri, 14 Jun 2024 11:28:02 +0200 Subject: [PATCH 3/4] fix test --- package.json | 2 +- react/__mocks__/vtex.css-handles.js | 117 +++++++++++++++++++++++ react/__tests__/ProfileContainer.test.js | 16 ++-- react/__tests__/ProfileRules.test.js | 5 +- 4 files changed, 126 insertions(+), 14 deletions(-) create mode 100644 react/__mocks__/vtex.css-handles.js diff --git a/package.json b/package.json index afb0638e..84d7e8df 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ ], "husky": { "hooks": { - "pre-push": "yarn locales:lint" + "pre-push": "yarn locales:lint && yarn test" } }, "jest": { diff --git a/react/__mocks__/vtex.css-handles.js b/react/__mocks__/vtex.css-handles.js new file mode 100644 index 00000000..794b1a29 --- /dev/null +++ b/react/__mocks__/vtex.css-handles.js @@ -0,0 +1,117 @@ +/* eslint-disable default-param-last */ +import React, { createContext, useContext, useMemo } from 'react' + +export const useCssHandles = cssHandles => { + const handles = {} + + cssHandles.forEach(handle => { + handles[handle] = handle + }) + + return { handles, withModifiers: withModifiersHelper(handles) } +} + +const withModifiersHelper = handles => (handle, modifier) => { + return applyModifiers(handles[handle], modifier) +} + +export const createCssHandlesContext = cssHandles => { + const Context = createContext({ + handles: cssHandles, + withModifiers: withModifiersHelper(cssHandles), + }) + + const useContextCssHandles = () => { + return useContext(Context) + } + + const CssHandlesProvider = ({ withModifiers, handles, children }) => { + const value = useMemo( + () => ({ + handles, + withModifiers, + }), + [withModifiers, handles] + ) + + return {children} + } + + return { useContextCssHandles, CssHandlesProvider } +} + +const validateModifier = modifier => { + if (typeof modifier !== 'string') { + console.error( + `Invalid modifier type on \`cssHandles.applyModifier\`. All modifiers should be strings, found "${modifier}" ` + ) + + return false + } + + /* This is not an error, so doesn't log any message, but should + * invalidate the current modifier and not include it */ + if (modifier === '') { + return false + } + + if (/[^A-z0-9-]/.test(modifier)) { + console.error( + `Invalid modifier on \`cssHandles.applyModifier\`. Modifiers should contain only letters, numbers or -` + ) + + return false + } + + return true +} + +export const applyModifiers = (handles, modifier) => { + const normalizedModifiers = + typeof modifier === 'string' ? [modifier] : modifier + + if (!Array.isArray(normalizedModifiers)) { + console.error( + 'Invalid modifier type on `cssHandles.applyModifier`. Please use either a string or an array of strings' + ) + + return handles + } + + const splitHandles = handles.split(' ') + + const modifiedHandles = normalizedModifiers + .map(currentModifier => { + const isValid = validateModifier(currentModifier) + + if (!isValid) { + return '' + } + + return splitHandles + .map(handle => `${handle}--${currentModifier}`) + .join(' ') + .trim() + }) + .filter(l => l.length > 0) + .join(' ') + .trim() + + return splitHandles.concat(modifiedHandles).join(' ').trim() +} + +export const withCssHandles = + (handles = [], options) => + Component => { + const EnhancedComponent = props => { + const { handles: cssHandles } = useCssHandles(handles, options) + + return + } + + const displayName = Component.displayName || Component.name || 'Component' + + EnhancedComponent.displayName = `withCssHandles(${displayName})` + + return EnhancedComponent + } \ No newline at end of file diff --git a/react/__tests__/ProfileContainer.test.js b/react/__tests__/ProfileContainer.test.js index f15e7b44..e45cc1f3 100644 --- a/react/__tests__/ProfileContainer.test.js +++ b/react/__tests__/ProfileContainer.test.js @@ -1,7 +1,7 @@ import React from 'react' import { shallowWithIntl, loadTranslation } from 'enzyme-react-intl' import ProfileContainer from '../ProfileContainer' -import ProfileField from '../ProfileField' +import ProfileFieldWrapper from '../ProfileFieldWrapper' import mockRules from '../__mocks__/rules' import mockProfile from '../__mocks__/profile' @@ -10,6 +10,7 @@ loadTranslation('../messages/pt.json') describe('ProfileContainer', () => { let wrapper let mockSubmit + beforeEach(() => { // Arrange mockSubmit = jest.fn() @@ -24,7 +25,7 @@ describe('ProfileContainer', () => { it('should render fields based on rules', () => { // Act - const result = wrapper.find(ProfileField) + const result = wrapper.find(ProfileFieldWrapper) // Assert expect(result).toHaveLength(4) @@ -32,14 +33,9 @@ describe('ProfileContainer', () => { it('should pass down profile data to fields', () => { // Act - const firstName = wrapper - .find(ProfileField) - .first() - .props().data.value - const lastName = wrapper - .find(ProfileField) - .last() - .props().data.value + const firstName = wrapper.find(ProfileFieldWrapper).first().props() + .data.value + const lastName = wrapper.find(ProfileFieldWrapper).last().props().data.value // Assert expect(firstName).toBe('John') diff --git a/react/__tests__/ProfileRules.test.js b/react/__tests__/ProfileRules.test.js index 09d86b7d..01bb82c8 100644 --- a/react/__tests__/ProfileRules.test.js +++ b/react/__tests__/ProfileRules.test.js @@ -1,5 +1,4 @@ import { IntlProvider } from 'react-intl' - import { prepareDateRules, filterDateType } from '../utils/dateRules' import defaultRules from '../rules/default' @@ -9,7 +8,7 @@ const { intl } = intlProvider.getChildContext() function getBirthDate(rules) { const preparedRules = prepareDateRules(rules, intl) const birthDateInitialized = preparedRules.personalFields.find( - rule => rule.name === 'birthDate', + (rule) => rule.name === 'birthDate', ) return birthDateInitialized } @@ -27,7 +26,7 @@ describe('ProfileRules aux functions', () => { const rules = defaultRules const birthDate = filterDateType(rules.personalFields)[0] - expect(birthDate.mask).toBeUndefined() + expect(birthDate.mask).toBeDefined() expect(birthDate.validate).toBeDefined() expect(birthDate.display).toBeUndefined() expect(birthDate.submit).toBeUndefined() From f2ebd5e9e5d6fdb076a3e2c5a4ceecf7b4764ee9 Mon Sep 17 00:00:00 2001 From: beatrizmaselli Date: Fri, 14 Jun 2024 11:32:35 +0200 Subject: [PATCH 4/4] add changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8fd2a30..b79882fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Added + +- CSS Handles based at field names for edit profile customizations at my account. + ## [3.16.6] - 2024-06-05 ### Added