diff --git a/CHANGELOG.md b/CHANGELOG.md index f78e3d8..64ae583 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- `migrationFrom` option. ## [0.2.1] - 2019-10-03 ### Changed diff --git a/docs/README.md b/docs/README.md index 56b7616..5d9c61c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,7 +4,7 @@ Utility for generating CSS handles for store components. 🚧 Under Construction 🚧 -### Usage +## Usage ```tsx import React, { FunctionComponent } from 'react' @@ -35,3 +35,14 @@ const Component: FunctionComponent = () => { ) } ``` + +### Options + +- `migrationFrom`: Adds additional CSS handles for cases where a component is migrating from another app. + +#### Usage: +```tsx +const CSS_HANDLES = ['container'] +const handles = useCssHandles(CSS_HANDLES, { migrationFrom: 'vtex.store-components@3.x' }) +// Returns { container: 'vtex-current-app-0-x-container vtex-store-components-3-x-container' } +``` diff --git a/react/__tests__/useCssHandles.test.tsx b/react/__tests__/useCssHandles.test.tsx index 665d965..5661130 100644 --- a/react/__tests__/useCssHandles.test.tsx +++ b/react/__tests__/useCssHandles.test.tsx @@ -70,5 +70,24 @@ describe('useCssHandles', () => { 'vtex-app-2-x-element2 vtex-app-2-x-element2--blockClass vtex-previous-app-2-x-element2 vtex-previous-app-2-x-element2--blockClass vtex-previous-app-3-x-element2 vtex-previous-app-3-x-element2--blockClass', }) }) + + it('doesnt repeat the migration if the current app happens to be the same as the migration one', () => { + const CSS_HANDLES = ['element1', 'element2'] + + const handles = useCssHandles(CSS_HANDLES, { + migrationFrom: [ + 'vtex.previous-app@2.0.0', + 'vtex.previous-app@3.0.0', + 'vtex.app@2.1.0', + ], + }) + + expect(handles).toStrictEqual({ + element1: + 'vtex-app-2-x-element1 vtex-app-2-x-element1--blockClass vtex-previous-app-2-x-element1 vtex-previous-app-2-x-element1--blockClass vtex-previous-app-3-x-element1 vtex-previous-app-3-x-element1--blockClass', + element2: + 'vtex-app-2-x-element2 vtex-app-2-x-element2--blockClass vtex-previous-app-2-x-element2 vtex-previous-app-2-x-element2--blockClass vtex-previous-app-3-x-element2 vtex-previous-app-3-x-element2--blockClass', + }) + }) }) }) diff --git a/react/useCssHandles.tsx b/react/useCssHandles.tsx index 60464b2..7be107f 100644 --- a/react/useCssHandles.tsx +++ b/react/useCssHandles.tsx @@ -9,11 +9,7 @@ interface CssHandlesOptions { migrationFrom?: string | string[] } -const generateCssHandles = ( - component: string, - handles: T, - modifiers?: string | string[] -) => { +const parseComponentName = (componentName: string) => { /* Matches until the first `.` or `@`. * Used to split something like `vtex.style-guide@2.0.1` into * `vtex`, `style-guide`, and `2`. */ @@ -23,12 +19,24 @@ const generateCssHandles = ( * provides 3 different results. Yeah I know. * There exists a `String.matchAll()` function but it's not * supported on Safari */ - const [vendor] = splitAppName.exec(component) || [null] - const [name] = splitAppName.exec(component) || [null] - const [major] = splitAppName.exec(component) || [null] + const [vendor] = splitAppName.exec(componentName) || [null] + const [name] = splitAppName.exec(componentName) || [null] + const [major] = splitAppName.exec(componentName) || [null] - const namespace = vendor && name && major && `${vendor}-${name}-${major}-x` + return { vendor, name, major } +} + +const normalizeComponentName = (componentName: string) => { + const { vendor, name, major } = parseComponentName(componentName) + + return vendor && name && major && `${vendor}-${name}-${major}-x` +} +const generateCssHandles = ( + namespace: string, + handles: T, + modifiers?: string | string[] +) => { return handles.reduce>((acc, handle) => { const isValid = !!namespace && validateCssHandle(handle) const transformedHandle = `${namespace}-${handle}` @@ -66,11 +74,24 @@ const useCssHandles = ( const values = useMemo>(() => { const { migrationFrom } = options - const components = [component] + const normalizedComponent = normalizeComponentName(component) + + const namespaces = normalizedComponent ? [normalizedComponent] : [] if (migrationFrom) { - components.push(...([] as string[]).concat(migrationFrom)) + const migrations = Array.isArray(migrationFrom) + ? migrationFrom + : [migrationFrom] + + const normalizedMigrations = migrations + .map(normalizeComponentName) + .filter( + current => !!current && current !== normalizedComponent + ) as string[] + + namespaces.push(...normalizedMigrations) } - return components + + return namespaces .map(component => generateCssHandles(component, handles, blockClass)) .reduce>( (acc: null | CssHandles, cur: CssHandles) => {