Skip to content

Commit 76da16c

Browse files
refactor(styles): diff view and code theme on shared tokens (#8069)
1 parent 93a6d02 commit 76da16c

13 files changed

Lines changed: 327 additions & 149 deletions

File tree

frontend/.storybook/docs-theme.scss

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// =============================================================================
88

99
// Light mode — fix invisible inline code
10-
.sbdocs code {
10+
.sbdocs code:not(.hljs) {
1111
color: #1a2634;
1212
}
1313

@@ -50,6 +50,15 @@
5050
color: var(--color-text-default, #ffffff);
5151
}
5252

53+
// Storybook marks rendered stories .sb-unstyled; the two rules above predate
54+
// that and were repainting any <pre> a component renders. Skip .hljs, which
55+
// brings its own surface.
56+
.sbdocs .sb-unstyled code:not(.hljs),
57+
.sbdocs .sb-unstyled pre:not(.hljs) {
58+
background-color: transparent;
59+
color: inherit;
60+
}
61+
5362
// Storybook Source/code block container and all its children
5463
.docblock-source,
5564
.docblock-source > * {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { Meta, StoryObj } from 'storybook'
2+
3+
import DiffEnabled from 'components/diff/DiffEnabled'
4+
5+
const meta: Meta<typeof DiffEnabled> = {
6+
component: DiffEnabled,
7+
parameters: {
8+
docs: {
9+
description: {
10+
component:
11+
'The diff view for a flag or segment override being turned on or off.',
12+
},
13+
},
14+
layout: 'padded',
15+
},
16+
title: 'Components/Diff/DiffEnabled',
17+
}
18+
19+
export default meta
20+
21+
type Story = StoryObj<typeof DiffEnabled>
22+
23+
export const OffToOn: Story = {
24+
args: { newValue: true, oldValue: false },
25+
}
26+
27+
export const OnToOff: Story = {
28+
args: { newValue: false, oldValue: true },
29+
}
30+
31+
export const Unchanged: Story = {
32+
args: { newValue: true, oldValue: true },
33+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import type { Meta, StoryObj } from 'storybook'
2+
3+
import DiffRow from 'components/diff/DiffRow'
4+
5+
const meta: Meta<typeof DiffRow> = {
6+
component: DiffRow,
7+
parameters: {
8+
docs: {
9+
description: {
10+
component:
11+
'One row of a diff: a marker cell beside the content that changed. ' +
12+
'Used by DiffEnabled and DiffString for the rows we build ourselves.',
13+
},
14+
},
15+
layout: 'padded',
16+
},
17+
title: 'Components/Diff/DiffRow',
18+
}
19+
20+
export default meta
21+
22+
type Story = StoryObj<typeof DiffRow>
23+
24+
export const Removed: Story = {
25+
args: { children: 'banner_size: small', state: 'removed' },
26+
}
27+
28+
export const Added: Story = {
29+
args: { children: 'banner_size: large', state: 'added' },
30+
}
31+
32+
export const Unchanged: Story = {
33+
args: { children: 'banner_size: large', state: 'unchanged' },
34+
}
35+
36+
export const Stacked: Story = {
37+
render: () => (
38+
<>
39+
<DiffRow state='removed'>banner_size: small</DiffRow>
40+
<DiffRow state='added'>banner_size: large</DiffRow>
41+
</>
42+
),
43+
}
44+
45+
export const LongContentScrolls: Story = {
46+
args: {
47+
children: 'banner_size: '.repeat(40),
48+
scrollable: true,
49+
state: 'unchanged',
50+
},
51+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import type { Meta, StoryObj } from 'storybook'
2+
3+
import DiffString from 'components/diff/DiffString'
4+
5+
const meta: Meta<typeof DiffString> = {
6+
component: DiffString,
7+
parameters: {
8+
docs: {
9+
description: {
10+
component:
11+
'The diff view for flag values (react-diff-viewer + Prism for JSON). ' +
12+
'Uses the same code colour tokens as the Highlight component, so it ' +
13+
'follows light/dark mode. Toggle the theme in the toolbar to QA.',
14+
},
15+
},
16+
layout: 'padded',
17+
},
18+
title: 'Components/Diff/DiffString',
19+
}
20+
21+
export default meta
22+
23+
type Story = StoryObj<typeof DiffString>
24+
25+
const oldJson = `{
26+
"id": "london-js",
27+
"title": "Hello London.js!",
28+
"description": "Join us at London.js and get insights on best practices!",
29+
"buttonText": "Register on Meetup",
30+
"isClosable": false
31+
}`
32+
33+
const newJson = `{
34+
"id": "london-js",
35+
"title": "Hello London.js!",
36+
"description": "Join us at London.js!",
37+
"buttonText": "Register now",
38+
"isClosable": true
39+
}`
40+
41+
export const ChangedJson: Story = {
42+
args: { newValue: newJson, oldValue: oldJson },
43+
}
44+
45+
export const ChangedString: Story = {
46+
args: { newValue: 'banner_size: large', oldValue: 'banner_size: small' },
47+
}
48+
49+
export const SameJson: Story = {
50+
args: { newValue: oldJson, oldValue: oldJson },
51+
}
52+
53+
// An empty old value renders as `""`, not a blank row.
54+
export const FromEmpty: Story = {
55+
args: { newValue: 'banner_size: large', oldValue: '' },
56+
}

frontend/web/components/diff/DiffEnabled.tsx

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { FC } from 'react'
2-
import classNames from 'classnames'
32
import Switch from 'components/Switch'
3+
import DiffRow from './DiffRow'
44

55
type DiffType = {
66
oldValue: boolean
@@ -21,22 +21,12 @@ const DiffEnabled: FC<DiffType> = ({ newValue, oldValue }) => {
2121
}
2222
return (
2323
<>
24-
<div className={'flex-row'}>
25-
<div className='react-diff-marker react-diff-marker--removed'>
26-
<pre>-</pre>
27-
</div>
28-
<div className='react-diff-line react-diff-line--removed pe-2 flex-fill'>
29-
<Switch checked={oldValue} />
30-
</div>
31-
</div>
32-
<div className={'flex-row'}>
33-
<div className='react-diff-marker react-diff-marker--added'>
34-
<pre>+</pre>
35-
</div>
36-
<div className='react-diff-line react-diff-line--added pe-2 flex-fill'>
37-
<Switch checked={newValue} />
38-
</div>
39-
</div>
24+
<DiffRow state='removed'>
25+
<Switch checked={oldValue} />
26+
</DiffRow>
27+
<DiffRow state='added'>
28+
<Switch checked={newValue} />
29+
</DiffRow>
4030
</>
4131
)
4232
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.diff-header {
2+
font-size: 13px;
3+
font-weight: 500;
4+
}

frontend/web/components/diff/DiffFeature.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
import DiffFeatureStateValues from './DiffFeatureStateValues'
1616
import DiffSegmentOverrides from './DiffSegmentOverrides'
1717
import DiffVariations from './DiffVariations'
18+
import './DiffFeature.scss'
1819
import InfoMessage from 'components/InfoMessage'
1920
import Icon from 'components/icons/Icon'
2021
import WarningMessage from 'components/WarningMessage'
@@ -104,13 +105,13 @@ const DiffFeature: FC<FeatureDiffType> = ({
104105
{(oldEnvName || newEnvName) && (
105106
<div className='d-flex gap-3 mb-3'>
106107
{oldEnvName && (
107-
<span className='diff-removed-header'>
108-
<span className='diff-sign'></span> {oldEnvName}
108+
<span className='diff-header py-1 px-2 rounded-sm bg-surface-danger text-danger'>
109+
<span className='me-1 fw-bold'></span> {oldEnvName}
109110
</span>
110111
)}
111112
{newEnvName && (
112-
<span className='diff-added-header'>
113-
<span className='diff-sign'>+</span> {newEnvName}
113+
<span className='diff-header py-1 px-2 rounded-sm bg-surface-success text-success'>
114+
<span className='me-1 fw-bold'>+</span> {newEnvName}
114115
</span>
115116
)}
116117
</div>
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// _react-diff.scss repeats these two values for the library's own rows.
2+
.diff-row {
3+
--diff-row-removed: oklch(from var(--red-500) l c h / 0.16);
4+
--diff-row-added: oklch(from var(--green-500) l c h / 0.16);
5+
6+
line-height: 31px;
7+
8+
&__marker {
9+
width: 31px;
10+
height: 31px;
11+
background-color: var(--color-surface-subtle);
12+
13+
> * {
14+
margin: 0;
15+
}
16+
}
17+
18+
&__content {
19+
flex: 1;
20+
min-width: 0;
21+
padding-right: 0.5rem;
22+
23+
.rc-switch {
24+
margin-bottom: 2px;
25+
}
26+
}
27+
28+
&--removed {
29+
.diff-row__marker,
30+
.diff-row__content {
31+
background-color: var(--diff-row-removed);
32+
}
33+
}
34+
35+
&--added {
36+
.diff-row__marker,
37+
.diff-row__content {
38+
background-color: var(--diff-row-added);
39+
}
40+
}
41+
42+
&--unchanged {
43+
background-color: var(--color-surface-subtle);
44+
font-size: 13px;
45+
46+
.diff-row__marker {
47+
opacity: 0;
48+
}
49+
50+
.diff-row__content {
51+
padding-right: 0;
52+
53+
pre {
54+
padding-left: 2px;
55+
background-color: var(--color-surface-subtle);
56+
font-family: var(--bs-font-monospace);
57+
font-size: 13px;
58+
line-height: 28px;
59+
}
60+
}
61+
}
62+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React, { FC, ReactNode } from 'react'
2+
import classNames from 'classnames'
3+
import './DiffRow.scss'
4+
5+
export type DiffRowState = 'added' | 'removed' | 'unchanged'
6+
7+
type DiffRowProps = {
8+
state: DiffRowState
9+
children: ReactNode
10+
// Scroll long content rather than widening the container.
11+
scrollable?: boolean
12+
className?: string
13+
}
14+
15+
const MARKERS: Record<DiffRowState, string> = {
16+
added: '+',
17+
removed: '-',
18+
unchanged: '',
19+
}
20+
21+
const DiffRow: FC<DiffRowProps> = ({
22+
children,
23+
className,
24+
scrollable,
25+
state,
26+
}) => (
27+
<div
28+
className={classNames(
29+
'diff-row d-flex align-items-center',
30+
`diff-row--${state}`,
31+
className,
32+
{ 'overflow-auto': scrollable },
33+
)}
34+
>
35+
<div className='diff-row__marker d-flex align-items-center justify-content-center flex-shrink-0'>
36+
<pre>{MARKERS[state]}</pre>
37+
</div>
38+
<div className='diff-row__content'>{children}</div>
39+
</div>
40+
)
41+
42+
export default DiffRow
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { default } from './DiffRow'
2+
export type { DiffRowState } from './DiffRow'

0 commit comments

Comments
 (0)