Skip to content

Commit e048f9e

Browse files
docs(seenBy): Create a story for the SeenByList component (#67664)
This is a demo of a component that fetches data from the server to render. It's kind of inefficient because we make multiple fetches at the same time. MemberListStore has some issues. Also, i'm failry sure that the `iconPosition` values are backwards, but that's another issue. This also includes some tweaks to allow `<Matrix>` to accept one item in the `selectedProps` array, makes it easier to splat out a list of possible prop values. <img width="1151" alt="SCR-20240325-opnu" src="https://github.com/getsentry/sentry/assets/187460/2563d40a-a9f3-402e-a8c3-2c11fe8cba37"> --------- Co-authored-by: Michelle Zhang <56095982+michellewzhang@users.noreply.github.com>
1 parent d8cc056 commit e048f9e

File tree

2 files changed

+169
-11
lines changed

2 files changed

+169
-11
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import {Fragment, useEffect} from 'react';
2+
3+
import Placeholder from 'sentry/components/placeholder';
4+
import SeenByList from 'sentry/components/seenByList';
5+
import JSXProperty from 'sentry/components/stories/jsxProperty';
6+
import Matrix from 'sentry/components/stories/matrix';
7+
import SizingWindow from 'sentry/components/stories/sizingWindow';
8+
import storyBook from 'sentry/stories/storyBook';
9+
import {useMembers} from 'sentry/utils/useMembers';
10+
import {useUser} from 'sentry/utils/useUser';
11+
12+
function useLoadedMembers() {
13+
const {members, loadMore, ...rest} = useMembers();
14+
15+
useEffect(() => {
16+
// `loadMore` is not referentially stable, so we cannot include it in the dependencies array
17+
loadMore();
18+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
19+
20+
return {members, loadMore, ...rest};
21+
}
22+
23+
export default storyBook(SeenByList, story => {
24+
story('Default', () => {
25+
const {members, fetching} = useLoadedMembers();
26+
27+
return (
28+
<SizingWindow display="block" style={{width: '50%'}}>
29+
{fetching ? <Placeholder /> : <SeenByList seenBy={members} />}
30+
</SizingWindow>
31+
);
32+
});
33+
34+
story('iconTooltip', () => {
35+
const {members, fetching} = useLoadedMembers();
36+
37+
return (
38+
<Fragment>
39+
<p>
40+
Default is{' '}
41+
<JSXProperty name="iconTooltip" value="People who have viewed this" />
42+
</p>
43+
<SizingWindow display="block" style={{width: '50%'}}>
44+
{fetching ? (
45+
<Placeholder />
46+
) : (
47+
<SeenByList
48+
seenBy={members}
49+
iconTooltip="These folks have all seen this record"
50+
/>
51+
)}
52+
</SizingWindow>
53+
</Fragment>
54+
);
55+
});
56+
57+
story('Always shows users except yourself', () => {
58+
const user = useUser();
59+
const {members, fetching} = useLoadedMembers();
60+
61+
return (
62+
<Fragment>
63+
<p>
64+
In this example we've explicitly put `user` at the start of the list, that's
65+
you! But it'll be filtered out. The idea is that that viewer is more interested
66+
in who else has seen a resource. On the issue stream, for example, we indicate
67+
if the viewer (you) has seen an issue by changing the font-weight to normal
68+
after viewed.
69+
</p>
70+
<SizingWindow display="block" style={{width: '50%'}}>
71+
{fetching ? <Placeholder /> : <SeenByList seenBy={[user, ...members]} />}
72+
</SizingWindow>
73+
</Fragment>
74+
);
75+
});
76+
77+
story('avatarSize', () => {
78+
const {members, fetching} = useLoadedMembers();
79+
80+
return (
81+
<Fragment>
82+
<p>
83+
Default is <JSXProperty name="avatarSize" value={28} />
84+
</p>
85+
{fetching ? (
86+
<Placeholder />
87+
) : (
88+
<Matrix
89+
sizingWindowProps={{display: 'block'}}
90+
render={SeenByList}
91+
selectedProps={['avatarSize']}
92+
propMatrix={{
93+
avatarSize: [12, 16, 20, 24, 28, 30],
94+
seenBy: [members],
95+
}}
96+
/>
97+
)}
98+
</Fragment>
99+
);
100+
});
101+
102+
story('maxVisibleAvatars', () => {
103+
const {members, fetching} = useLoadedMembers();
104+
105+
return (
106+
<Fragment>
107+
<p>
108+
Default is <JSXProperty name="maxVisibleAvatars" value={10} />
109+
</p>
110+
{fetching ? (
111+
<Placeholder />
112+
) : (
113+
<Matrix
114+
sizingWindowProps={{display: 'block'}}
115+
render={SeenByList}
116+
selectedProps={['maxVisibleAvatars']}
117+
propMatrix={{
118+
maxVisibleAvatars: [10, 5, 3, 1],
119+
seenBy: [members],
120+
}}
121+
/>
122+
)}
123+
</Fragment>
124+
);
125+
});
126+
127+
story('iconPosition', () => {
128+
const {members, fetching} = useLoadedMembers();
129+
130+
return (
131+
<Fragment>
132+
<p>
133+
Default is <JSXProperty name="iconPosition" value="left" />
134+
</p>
135+
{fetching ? (
136+
<Placeholder />
137+
) : (
138+
<Matrix
139+
sizingWindowProps={{display: 'block'}}
140+
render={SeenByList}
141+
selectedProps={['iconPosition']}
142+
propMatrix={{
143+
iconPosition: ['left', 'right'],
144+
seenBy: [members],
145+
}}
146+
/>
147+
)}
148+
</Fragment>
149+
);
150+
});
151+
});

static/app/components/stories/matrix.tsx

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export type PropMatrix<P extends RenderProps> = Partial<{
1515
interface Props<P extends RenderProps> {
1616
propMatrix: PropMatrix<P>;
1717
render: ElementType<P>;
18-
selectedProps: [keyof P, keyof P];
18+
selectedProps: [keyof P] | [keyof P, keyof P];
1919
sizingWindowProps?: SizingWindowProps;
2020
}
2121

@@ -32,21 +32,22 @@ export default function Matrix<P extends RenderProps>({
3232
);
3333

3434
const values1 = propMatrix[selectedProps[0]] ?? [];
35-
const values2 = propMatrix[selectedProps[1]] ?? [];
35+
const values2 = selectedProps.length === 2 ? propMatrix[selectedProps[1]] : undefined;
3636

3737
const items = values1.flatMap(value1 => {
3838
const label = (
3939
<div>
4040
<JSXProperty name={String(selectedProps[0])} value={value1} />
4141
</div>
4242
);
43-
const content = values2.map(value2 => {
43+
44+
const content = (values2 ?? ['']).map(value2 => {
4445
return item(
4546
render,
4647
{
4748
...defaultValues,
4849
[selectedProps[0]]: value1,
49-
[selectedProps[1]]: value2,
50+
...(selectedProps.length === 2 ? {[selectedProps[1]]: value2} : {}),
5051
},
5152
sizingWindowProps
5253
);
@@ -56,17 +57,23 @@ export default function Matrix<P extends RenderProps>({
5657

5758
return (
5859
<div>
59-
<h4 style={{margin: 0}}>
60-
<samp>{selectedProps[0] as string | number}</samp> vs{' '}
61-
<samp>{selectedProps[1] as string | number}</samp>
62-
</h4>
60+
{selectedProps.length === 2 ? (
61+
<h4 style={{margin: 0}}>
62+
<samp>{selectedProps[0] as string | number}</samp> vs{' '}
63+
<samp>{selectedProps[1] as string | number}</samp>
64+
</h4>
65+
) : (
66+
<h4 style={{margin: 0}}>
67+
<samp>{selectedProps[0] as string | number}</samp>
68+
</h4>
69+
)}
6370
<Grid
6471
style={{
65-
gridTemplateColumns: `max-content repeat(${values2.length}, max-content)`,
72+
gridTemplateColumns: `max-content repeat(${values2?.length ?? 1}, max-content)`,
6673
}}
6774
>
68-
<div key="space-head" />
69-
{values2.map(value2 => (
75+
{values2 ? <div key="space-head" /> : null}
76+
{values2?.map(value2 => (
7077
<div key={`title-2-${value2}`}>
7178
<JSXProperty name={String(selectedProps[1])} value={value2} />
7279
</div>

0 commit comments

Comments
 (0)