Skip to content

Commit

Permalink
style(toolbar): update feature flag panel css (#76102)
Browse files Browse the repository at this point in the history
- relates to #75636
- modify feature flag css according to the figma. main changes:
- move "clear overrides" button from above search bar to the bottom of
the "overrides" tab.
    - Use x icon and "remove all" for the clear overrides button
    - make a "+ add flag" button in header
    - remove the gray "name value" header

before:
<img width="255" alt="SCR-20240813-mmtm"
src="https://github.com/user-attachments/assets/17fdee18-25fc-40ab-a26f-90d0e9c0fd20">
<img width="264" alt="SCR-20240813-mmul"
src="https://github.com/user-attachments/assets/7f8f325c-cee1-4049-b4c4-d7cd58e95a08">

after:


https://github.com/user-attachments/assets/acc7ede1-3f03-4999-969c-973ac410dd8c
  • Loading branch information
michellewzhang committed Aug 15, 2024
1 parent 90d8115 commit 94c3083
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ import {useContext, useState} from 'react';
import {Button} from 'sentry/components/button';
import Input from 'sentry/components/input';
import Switch from 'sentry/components/switchButton';
import {IconAdd} from 'sentry/icons';

import useConfiguration from '../../hooks/useConfiguration';
import {AnalyticsContext} from '../analyticsProvider';

import {useFeatureFlagsContext} from './featureFlagsContext';

export default function CustomOverride() {
export default function CustomOverride({
setComponentActive,
}: {
setComponentActive: (value: boolean) => void;
}) {
const {eventName, eventKey} = useContext(AnalyticsContext);
const {trackAnalytics} = useConfiguration();
const {setOverride} = useFeatureFlagsContext();
Expand All @@ -22,7 +27,7 @@ export default function CustomOverride() {
css={[
{
display: 'grid',
gridTemplateColumns: 'auto max-content auto',
gridTemplateColumns: 'auto max-content max-content',
alignItems: 'center',
justifyItems: 'space-between',
gap: 'var(--space100)',
Expand All @@ -31,6 +36,7 @@ export default function CustomOverride() {
onSubmit={e => {
e.preventDefault();
setOverride(name, isActive);
setComponentActive(false);
setName('');
setIsActive(false);
trackAnalytics?.({
Expand All @@ -46,13 +52,15 @@ export default function CustomOverride() {
onChange={e => setName(e.target.value.toLowerCase())}
/>
<Switch
size="lg"
isActive={isActive}
toggle={() => {
setIsActive(!isActive);
}}
css={{background: 'white'}}
/>
<Button size="xs" type="submit">
Add Override
<Button size="xs" type="submit" css={{width: '28px'}} disabled={!name.length}>
<IconAdd />
</Button>
</form>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Switch from 'sentry/components/switchButton';

import useConfiguration from '../../hooks/useConfiguration';
import {inlineLinkCss} from '../../styles/link';
import {panelInsetContentCss} from '../../styles/panel';
import {verticalPaddingCss} from '../../styles/panel';
import {smallCss} from '../../styles/typography';
import type {FlagValue} from '../../types';

Expand All @@ -23,7 +23,12 @@ export default function FeatureFlagItem({flag}: {flag: FeatureFlag}) {

return (
<Fragment>
<Cell css={[panelInsetContentCss, {alignItems: 'flex-start'}]}>
<Cell
css={[
verticalPaddingCss,
{alignItems: 'flex-start', marginLeft: 'var(--space200)'},
]}
>
{featureFlags?.urlTemplate?.(flag.name) ? (
<ExternalLink
css={[smallCss, inlineLinkCss]}
Expand All @@ -41,7 +46,7 @@ export default function FeatureFlagItem({flag}: {flag: FeatureFlag}) {
<span>{flag.name}</span>
)}
</Cell>
<Cell>
<Cell css={{marginRight: 'var(--space200)', justifyContent: 'center'}}>
<FlagValueInput flag={flag} />
</Cell>
</Fragment>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import {type Dispatch, Fragment, type SetStateAction, useState} from 'react';

import {Button} from 'sentry/components/button';
import {resetButtonCss, resetFlexRowCss} from 'sentry/components/devtoolbar/styles/reset';
import Input from 'sentry/components/input';
import {PanelTable} from 'sentry/components/panels/panelTable';
import {SegmentedControl} from 'sentry/components/segmentedControl';
import {IconChevron, IconClose} from 'sentry/icons';

import {panelInsetContentCss, panelSectionCss} from '../../styles/panel';
import {
buttonRightCss,
panelHeadingRightCss,
panelInsetContentCss,
panelSectionCss,
panelSectionCssNoBorder,
} from '../../styles/panel';
import {smallCss} from '../../styles/typography';
import AnalyticsProvider from '../analyticsProvider';
import PanelLayout from '../panelLayout';
Expand All @@ -19,20 +27,54 @@ type Prefilter = 'all' | 'overrides';
export default function FeatureFlagsPanel() {
const [prefilter, setPrefilter] = useState<Prefilter>('all');
const [searchTerm, setSearchTerm] = useState('');
const [isAddFlagActive, setIsAddFlagActive] = useState(false);

return (
<FeatureFlagsContextProvider>
<PanelLayout title="Feature Flags">
<PanelLayout
title="Feature Flags"
titleRight={
<button
aria-label="Add Flag Override"
css={[resetButtonCss, panelHeadingRightCss]}
title="Add Flag Override"
onClick={() => setIsAddFlagActive(!isAddFlagActive)}
>
<span css={buttonRightCss}>
{isAddFlagActive ? (
<IconChevron direction="up" size="xs" />
) : (
<IconChevron direction="down" size="xs" />
)}
Add Flag
</span>
</button>
}
>
{isAddFlagActive && (
<div
css={[
smallCss,
panelSectionCss,
panelInsetContentCss,
{background: 'var(--surface200)', padding: 'var(--space150)'},
]}
>
<AnalyticsProvider keyVal="custom-override" nameVal="Custom Override">
<CustomOverride setComponentActive={setIsAddFlagActive} />
</AnalyticsProvider>
</div>
)}
<div css={{display: 'grid', gridTemplateRows: 'auto auto 1fr auto', flexGrow: 1}}>
<IsDirtyMessage />
<div
css={[
smallCss,
panelSectionCss,
panelSectionCssNoBorder,
panelInsetContentCss,
{
display: 'grid',
gridTemplateAreas: "'segments clear' 'search search'",
gridTemplateAreas: "'search segments'",
gap: 'var(--space100)',
},
]}
Expand All @@ -52,11 +94,6 @@ export default function FeatureFlagsPanel() {
</AnalyticsProvider>
</div>
</div>
<div css={[smallCss, panelSectionCss, panelInsetContentCss]}>
<AnalyticsProvider keyVal="custom-override" nameVal="Custom Override">
<CustomOverride />
</AnalyticsProvider>
</div>
</div>
</PanelLayout>
</FeatureFlagsContextProvider>
Expand All @@ -67,7 +104,9 @@ function IsDirtyMessage() {
const {isDirty} = useFeatureFlagsContext();

return isDirty ? (
<div css={[smallCss, panelSectionCss, panelInsetContentCss]}>
<div
css={[smallCss, panelSectionCss, panelInsetContentCss, {color: 'var(--gray300)'}]}
>
<span>Reload to see changes</span>
</div>
) : (
Expand All @@ -84,36 +123,26 @@ function Filters({
setPrefilter: Dispatch<SetStateAction<Prefilter>>;
setSearchTerm: Dispatch<SetStateAction<string>>;
}) {
const {clearOverrides} = useFeatureFlagsContext();
return (
<Fragment>
<div css={{gridArea: 'segments'}}>
<SegmentedControl<Prefilter> onChange={setPrefilter} size="xs" value={prefilter}>
<SegmentedControl.Item key="all">All Flags</SegmentedControl.Item>
<SegmentedControl.Item key="overrides">Overrides Only</SegmentedControl.Item>
<SegmentedControl.Item key="all">All</SegmentedControl.Item>
<SegmentedControl.Item key="overrides">Overrides</SegmentedControl.Item>
</SegmentedControl>
</div>
<Button
size="xs"
onClick={() => {
clearOverrides();
}}
css={{gridArea: 'clear'}}
>
Clear Overrides
</Button>
<Input
css={{gridArea: 'search'}}
onChange={e => setSearchTerm(e.target.value.toLowerCase())}
placeholder="Search flags"
placeholder="Search"
size="xs"
/>
</Fragment>
);
}

function FlagTable({prefilter, searchTerm}: {prefilter: string; searchTerm: string}) {
const {featureFlagMap} = useFeatureFlagsContext();
function FlagTable({prefilter, searchTerm}: {prefilter: Prefilter; searchTerm: string}) {
const {featureFlagMap, clearOverrides} = useFeatureFlagsContext();

const filtered = Object.fromEntries(
Object.entries(featureFlagMap)?.filter(([name, {value, override}]) => {
Expand All @@ -128,32 +157,74 @@ function FlagTable({prefilter, searchTerm}: {prefilter: string; searchTerm: stri
const names = Object.keys(filtered).sort();

return (
<PanelTable
css={[
panelSectionCss,
{
flexGrow: 1,
margin: 0,
borderRadius: 0,
border: 'none',
padding: 0,
'& > :first-child': {
minHeight: 'unset',
padding: 'var(--space50) var(--space150)',
<span>
<PanelTable
disablePadding
disableHeaders
css={[
{
flexGrow: 1,
margin: 0,
borderRadius: 0,
border: 'none',
padding: 0,
'& > :first-child': {
minHeight: 'unset',
},
},
},
]}
headers={[
<Fragment key="name">Name</Fragment>,
<Fragment key="value">Value</Fragment>,
]}
stickyHeaders
>
{names?.map(name => (
<AnalyticsProvider key={name} keyVal="flag-item" nameVal="Flag Item">
<FeatureFlagItem flag={{name, ...filtered[name]}} />
</AnalyticsProvider>
))}
</PanelTable>
]}
headers={[undefined, undefined]}
stickyHeaders
>
{names?.map(name => (
<AnalyticsProvider key={name} keyVal="flag-item" nameVal="Flag Item">
<FeatureFlagItem flag={{name, ...filtered[name]}} />
</AnalyticsProvider>
))}
</PanelTable>
{!names.length && (
<div
css={[
smallCss,
panelSectionCssNoBorder,
panelInsetContentCss,
{display: 'block', textAlign: 'center', color: 'var(--gray300)'},
]}
>
No flags to display
</div>
)}
{prefilter === 'overrides' && Boolean(names.length) && (
<div
css={[
smallCss,
panelSectionCssNoBorder,
panelInsetContentCss,
{display: 'block', textAlign: 'center'},
]}
>
<Button
size="xs"
css={{width: '100%'}}
onClick={() => {
clearOverrides();
}}
>
<span
css={[
resetFlexRowCss,
{
gap: 'var(--space75)',
alignItems: 'center',
justifyContent: 'center',
},
]}
>
<IconClose isCircled size="xs" /> Remove All
</span>
</Button>
</div>
)}
</span>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
listItemPlaceholderWrapperCss,
} from '../../styles/listItem';
import {
buttonRightCss,
panelDescCss,
panelHeadingRightCss,
panelInsetContentCss,
Expand Down Expand Up @@ -68,15 +69,7 @@ export default function FeedbackPanel() {
ref={buttonRef}
title="Submit Feedback"
>
<span
css={{
display: 'flex',
gap: 'var(--space75)',
alignItems: 'center',
color: 'var(--purple300)',
fontWeight: 'bold',
}}
>
<span css={buttonRightCss}>
<IconMegaphone size="xs" />
Report Bug
</span>
Expand Down
2 changes: 2 additions & 0 deletions static/app/components/devtoolbar/styles/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export const globalCss = css`
--pink200: rgba(249, 26, 138, 0.5);
--pink100: rgba(249, 26, 138, 0.09);
--surface200: #faf9fb;
--z-index: 100000;
color: var(--gray400);
Expand Down
17 changes: 17 additions & 0 deletions static/app/components/devtoolbar/styles/panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,31 @@ export const panelSectionCss = css`
}
`;

export const panelSectionCssNoBorder = css`
position: relative;
padding-block: var(--space150);
`;

export const panelInsetContentCss = css`
padding-inline: var(--space150);
`;

export const verticalPaddingCss = css`
padding: var(--space150) 0;
`;

export const panelDescCss = css`
color: var(--gray300);
font-weight: bold;
margin: 0 var(--space150);
text-align: left;
padding-top: var(--space200);
`;

export const buttonRightCss = css`
display: flex;
gap: var(--space75);
align-items: center;
color: var(--purple300);
font-weight: bold;
`;
Loading

0 comments on commit 94c3083

Please sign in to comment.