Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/sharp-phones-film.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/polaris-migrator': patch
---

Add namespace support to the `replace-static-breakpoint-mixins` migration
13 changes: 13 additions & 0 deletions polaris-migrator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ Replace the legacy Sass `spacing()` function with the supported CSS custom prope
npx @shopify/polaris-migrator replace-sass-spacing <path>
```

### `replace-static-breakpoint-mixins`

Replace legacy static breakpoint mixins with the new Polaris [media query variables](https://github.com/Shopify/polaris/blob/main/documentation/guides/migrating-from-v9-to-v10.md#media-query-variables).

```diff
- @include page-content-when-layout-not-stacked {}
+ @media #{$p-breakpoints-md-up} {}
```

```sh
npx @shopify/polaris-migrator replace-static-breakpoint-mixins <path>
```

## Creating a migration

### Setup
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {FileInfo} from 'jscodeshift';
import {FileInfo, API, Options} from 'jscodeshift';
import postcss, {Plugin} from 'postcss';

import {NamespaceOptions, getNamespacePattern} from '../../utilities/sass';
import {isKeyOf} from '../../utilities/type-guards';

/** Mapping of static breakpoint mixins from old to new */
Expand All @@ -22,25 +23,39 @@ const staticBreakpointMixins = {
'after-topbar-sheet': '#{$p-breakpoints-sm-up}',
};

const plugin = (): Plugin => ({
postcssPlugin: 'ReplaceStaticBreakpointMixins',
AtRule(atRule) {
if (atRule.name !== 'include') return;
interface PluginOptions extends Options, NamespaceOptions {}

// Extract mixin name e.g. name from `@include name;` or `@include name();`
const mixinName = atRule.params.match(/^([a-zA-Z0-9_-]+)/)?.[1];
const plugin = (options: PluginOptions = {}): Plugin => {
const namespacePattern = getNamespacePattern(options);

if (!isKeyOf(staticBreakpointMixins, mixinName)) return;
const namespacedMixinRegExp = new RegExp(
String.raw`^${namespacePattern}([\w-]+)`,
);

atRule.assign({
name: 'media',
params: staticBreakpointMixins[mixinName],
});
},
});
return {
postcssPlugin: 'ReplaceStaticBreakpointMixins',
AtRule(atRule) {
if (atRule.name !== 'include') return;

export default function replaceStaticBreakpointMixins(fileInfo: FileInfo) {
return postcss(plugin()).process(fileInfo.source, {
// Extract mixin name e.g. name from `@include name;` or `@include name();`
const mixinName = atRule.params.match(namespacedMixinRegExp)?.[1];

if (!isKeyOf(staticBreakpointMixins, mixinName)) return;

atRule.assign({
name: 'media',
params: staticBreakpointMixins[mixinName],
});
},
};
};

export default function replaceStaticMixinsWithDeclarations(
fileInfo: FileInfo,
_: API,
options: Options,
) {
return postcss(plugin(options)).process(fileInfo.source, {
syntax: require('postcss-scss'),
}).css;
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import {check} from '../../../utilities/testUtils';

const migration = 'replace-static-breakpoint-mixins';
const fixtures = ['replace-static-breakpoint-mixins'];
const fixtures = ['replace-static-breakpoint-mixins', 'with-namespace'];

for (const fixture of fixtures) {
check(__dirname, {
fixture,
migration,
extension: 'scss',
options: {
namespace: fixture.includes('with-namespace')
? 'legacy-polaris-v8'
: undefined,
},
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
@use 'global-styles/legacy-polaris-v8';

.root {
@include legacy-polaris-v8.page-content-when-partially-condensed {
padding-bottom: spacing(loose);

&.Active {
border-bottom: rem(3px) solid color('ink');
}

@include legacy-polaris-v8.page-content-when-not-partially-condensed {
padding: 0 spacing(extra-loose);
}
}

@include legacy-polaris-v8.page-content-when-fully-condensed {
margin: 0 spacing(loose) spacing(tight);
}

@include legacy-polaris-v8.page-content-when-not-fully-condensed {
border-radius: spacing(tight);
}

@include legacy-polaris-v8.page-when-not-max-width {
border-radius: spacing(tight);
}

@include legacy-polaris-v8.page-content-when-layout-stacked {
@include legacy-polaris-v8.stacked-elements;
}

@include legacy-polaris-v8.page-content-when-layout-not-stacked {
border-radius: spacing(tight);
}

@include legacy-polaris-v8.page-after-resource-list-small {
border-radius: spacing(tight);
}

@include legacy-polaris-v8.page-before-resource-list-small {
border-radius: spacing(tight);
}

@include legacy-polaris-v8.after-topbar-sheet {
max-width: $content-max-width + (2 * spacing(extra-loose));

> li {
margin-right: spacing();
}

&.TwoColumn > li {
flex: 0 calc(50% - #{spacing(extra-loose)});
}
}

@include legacy-polaris-v8.frame-with-nav-when-not-max-width {
padding: spacing(tight) 0;
}

@mixin global-nav-offset {
@include legacy-polaris-v8.frame-when-nav-displayed {
left: $global-nav-width;
width: calc(100% - #{$global-nav-width});
}
}

.Large {
@include legacy-polaris-v8.frame-when-nav-displayed {
width: $sheet-desktop-width-large;
}
}

@include legacy-polaris-v8.frame-when-nav-displayed {
@include legacy-polaris-v8.breakpoint-after(
layout-width(page-content, not-condensed) + layout-width(nav)
) {
@content;
}
}

@include legacy-polaris-v8.frame-when-nav-hidden {
@include legacy-polaris-v8.stick-to-bottom-container;
}

.MinimizableVideoToolbar {
&.Inline {
@include legacy-polaris-v8.frame-when-nav-hidden {
display: none;
}
}
}

@include legacy-polaris-v8.frame-when-nav-hidden {
@include legacy-polaris-v8.page-content-when-not-partially-condensed {
width: rem(200px);
}
}

@mixin breakpoint-tablet-not-condensed {
@include legacy-polaris-v8.frame-when-nav-hidden {
@include legacy-polaris-v8.page-content-when-not-partially-condensed {
@content;
}
}

@include legacy-polaris-v8.frame-when-nav-displayed {
@include legacy-polaris-v8.breakpoint-after(
layout-width(page-content, not-condensed) + layout-width(nav)
) {
@content;
}
}
}

> *:not(:last-child) {
@include legacy-polaris-v8.when-typography-condensed {
margin-bottom: var(--p-space-4);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
@use 'global-styles/legacy-polaris-v8';

.root {
@media #{$p-breakpoints-lg-down} {
padding-bottom: spacing(loose);

&.Active {
border-bottom: rem(3px) solid color('ink');
}

@media #{$p-breakpoints-md-up} {
padding: 0 spacing(extra-loose);
}
}

@media #{$p-breakpoints-sm-down} {
margin: 0 spacing(loose) spacing(tight);
}

@media #{$p-breakpoints-sm-up} {
border-radius: spacing(tight);
}

@media #{$p-breakpoints-lg-down} {
border-radius: spacing(tight);
}

@media #{$p-breakpoints-lg-down} {
@include legacy-polaris-v8.stacked-elements;
}

@media #{$p-breakpoints-md-up} {
border-radius: spacing(tight);
}

@media #{$p-breakpoints-sm-up} {
border-radius: spacing(tight);
}

@media #{$p-breakpoints-sm-down} {
border-radius: spacing(tight);
}

@media #{$p-breakpoints-sm-up} {
max-width: $content-max-width + (2 * spacing(extra-loose));

> li {
margin-right: spacing();
}

&.TwoColumn > li {
flex: 0 calc(50% - #{spacing(extra-loose)});
}
}

@media #{$p-breakpoints-lg-down} {
padding: spacing(tight) 0;
}

@mixin global-nav-offset {
@media #{$p-breakpoints-md-up} {
left: $global-nav-width;
width: calc(100% - #{$global-nav-width});
}
}

.Large {
@media #{$p-breakpoints-md-up} {
width: $sheet-desktop-width-large;
}
}

@media #{$p-breakpoints-md-up} {
@include legacy-polaris-v8.breakpoint-after(
layout-width(page-content, not-condensed) + layout-width(nav)
) {
@content;
}
}

@media #{$p-breakpoints-md-down} {
@include legacy-polaris-v8.stick-to-bottom-container;
}

.MinimizableVideoToolbar {
&.Inline {
@media #{$p-breakpoints-md-down} {
display: none;
}
}
}

@media #{$p-breakpoints-md-down} {
@media #{$p-breakpoints-md-up} {
width: rem(200px);
}
}

@mixin breakpoint-tablet-not-condensed {
@media #{$p-breakpoints-md-down} {
@media #{$p-breakpoints-md-up} {
@content;
}
}

@media #{$p-breakpoints-md-up} {
@include legacy-polaris-v8.breakpoint-after(
layout-width(page-content, not-condensed) + layout-width(nav)
) {
@content;
}
}
}

> *:not(:last-child) {
@media #{$p-breakpoints-md-down} {
margin-bottom: var(--p-space-4);
}
}
}
14 changes: 11 additions & 3 deletions polaris-migrator/src/utilities/sass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,16 @@ import {toPx} from '@shopify/polaris-tokens';

import {isKeyOf} from './type-guards';

const defaultNamespace = '';

function getNamespace(options?: NamespaceOptions) {
return options?.namespace || '';
return options?.namespace || defaultNamespace;
}

export function getNamespacePattern(options?: NamespaceOptions) {
const namespace = getNamespace(options);

return namespace ? String.raw`${namespace}\.` : defaultNamespace;
}

export interface NamespaceOptions {
Expand Down Expand Up @@ -156,10 +164,10 @@ export function replaceRemFunction(
map: ReplaceRemFunctionMap,
options?: NamespaceOptions,
): void {
const namespacedRemPattern = namespace('rem', options).replace('.', '\\.');
const namespacePattern = getNamespacePattern(options);

const namespacedRemFunctionRegExp = new RegExp(
String.raw`^${namespacedRemPattern}\(\s*([\d.]+)(px)?\s*\)\s*$`,
String.raw`^${namespacePattern}rem\(\s*([\d.]+)(px)?\s*\)\s*$`,
'g',
);

Expand Down