Skip to content

Commit 3ced5cf

Browse files
committed
Refactor StudioImmersiveModal and StudioPage for improved styling and structure; enhance ChannelDetailsModal tests for better clarity and functionality
1 parent 9dc051a commit 3ced5cf

File tree

3 files changed

+63
-93
lines changed

3 files changed

+63
-93
lines changed

contentcuration/contentcuration/frontend/shared/views/StudioImmersiveModal.vue

Lines changed: 12 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
>
77
<KToolbar
88
:textColor="dark ? 'white' : 'black'"
9-
:style="toolbarStyle"
9+
:style="{ backgroundColor: toolbarBackgroundColor }"
1010
>
1111
<template #icon>
1212
<KIconButton
@@ -20,7 +20,7 @@
2020
</template>
2121

2222
<template #default>
23-
<span class="notranslate toolbar-title">
23+
<span class="notranslate">
2424
<slot name="header">{{ title }}</slot>
2525
</span>
2626
</template>
@@ -29,10 +29,12 @@
2929
<slot name="action"></slot>
3030
</template>
3131
</KToolbar>
32+
3233
<StudioOfflineAlert :offset="64" />
34+
3335
<StudioPage
34-
class="modal-content"
3536
:offline="offline"
37+
:marginTop="contentMarginTop"
3638
>
3739
<slot></slot>
3840
</StudioPage>
@@ -76,19 +78,13 @@
7678
...mapState({
7779
offline: state => !state.connection.online,
7880
}),
79-
toolbarStyle() {
80-
const backgroundColor =
81-
this.color === 'appBarDark'
82-
? this.$themePalette.grey.v_900
83-
: this.$themeTokens[this.color] || this.color;
84-
return {
85-
backgroundColor,
86-
position: 'fixed',
87-
top: 0,
88-
left: 0,
89-
right: 0,
90-
zIndex: 17,
91-
};
81+
toolbarBackgroundColor() {
82+
return this.color === 'appBarDark'
83+
? this.$themePalette.grey.v_900
84+
: this.$themeTokens[this.color] || this.color;
85+
},
86+
contentMarginTop() {
87+
return this.offline ? 112 : 64;
9288
},
9389
},
9490
watch: {
@@ -134,25 +130,8 @@
134130
bottom: 0;
135131
left: 0;
136132
z-index: 17;
137-
display: flex;
138-
flex-direction: column;
139133
background-color: white;
140-
}
141-
142-
.modal-content {
143-
flex: 1;
144-
width: 100%;
145134
overflow-y: auto;
146135
}
147136
148-
.toolbar-title {
149-
display: block;
150-
margin-inline-start: 16px;
151-
margin-inline-end: 16px;
152-
overflow: hidden;
153-
text-overflow: ellipsis;
154-
white-space: nowrap;
155-
max-width: calc(100% - 80px);
156-
}
157-
158137
</style>

contentcuration/contentcuration/frontend/shared/views/StudioPage.vue

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
<template>
22

3-
<main class="studio-page-outer">
3+
<main
4+
class="studio-page-outer"
5+
:style="outerStyle"
6+
>
47
<div
58
class="studio-page-inner"
69
:style="innerStyle"
@@ -29,12 +32,22 @@
2932
paddingRight: `${paddingX.value}px`,
3033
paddingTop: `${paddingTop.value}px`,
3134
maxWidth: windowIsLarge.value ? '1000px' : '100%',
35+
margin: '0 auto',
3236
}));
3337
34-
return { innerStyle };
38+
const outerStyle = computed(() => {
39+
const marginTop = props.marginTop !== null ? props.marginTop : 104;
40+
return {
41+
marginTop: `${marginTop}px`,
42+
height: `calc(100vh - ${marginTop}px)`,
43+
};
44+
});
45+
46+
return { innerStyle, outerStyle };
3547
},
3648
props: {
3749
offline: { type: Boolean, default: false },
50+
marginTop: { type: Number, default: null },
3851
},
3952
};
4053
@@ -45,8 +58,6 @@
4558
4659
.studio-page-outer {
4760
width: 100%;
48-
height: calc(100vh - 104px);
49-
margin-top: 104px;
5061
margin-bottom: 16px;
5162
overflow-x: hidden;
5263
overflow-y: auto;

contentcuration/contentcuration/frontend/shared/views/channel/__tests__/channelDetailsModal.spec.js

Lines changed: 36 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,29 @@ const renderComponent = (options = {}) => {
9191
stubs: {
9292
StudioImmersiveModal: {
9393
template: `
94-
<div>
95-
<button data-test="close" @click="$emit('input', false)">Close</button>
94+
<div v-if="value" data-testid="modal-wrapper">
95+
<button data-test="close" @click="handleClose">Close</button>
9696
<div><slot name="header"></slot></div>
9797
<slot></slot>
9898
</div>
9999
`,
100100
props: ['value'],
101+
methods: {
102+
handleClose() {
103+
this.$emit('input', false);
104+
},
105+
},
106+
mounted() {
107+
const handleKeyDown = event => {
108+
if (event.key === 'Escape') {
109+
this.$emit('input', false);
110+
}
111+
};
112+
document.addEventListener('keydown', handleKeyDown);
113+
this.$once('hook:beforeDestroy', () => {
114+
document.removeEventListener('keydown', handleKeyDown);
115+
});
116+
},
101117
},
102118
StudioLargeLoader: {
103119
template: '<div data-testid="loader">Loading...</div>',
@@ -110,25 +126,22 @@ const renderComponent = (options = {}) => {
110126
template: `
111127
<button :data-test="$attrs['data-test']">
112128
{{ text }}
113-
<div v-if="hasDropdown"><slot name="menu"></slot></div>
114129
</button>
115130
`,
116131
props: ['text', 'primary', 'hasDropdown'],
117132
},
118133
KDropdownMenu: {
119-
template: `
120-
<div data-testid="dropdown-menu">
121-
<button
122-
v-for="option in options"
123-
:key="option.value"
124-
@click="$emit('select', option)"
125-
>
126-
{{ option.label }}
127-
</button>
128-
</div>
129-
`,
134+
template: '<div></div>',
130135
props: ['options'],
131136
},
137+
StudioPage: {
138+
template: '<div><slot></slot></div>',
139+
props: ['offline', 'marginTop'],
140+
},
141+
StudioOfflineAlert: {
142+
template: '<div></div>',
143+
props: ['offset'],
144+
},
132145
},
133146
mocks: {
134147
$analytics: {
@@ -157,7 +170,7 @@ describe('ChannelDetailsModal', () => {
157170
});
158171
});
159172

160-
it('clicking close button should close the modal', async () => {
173+
it('should close modal when close button is clicked', async () => {
161174
const user = userEvent.setup();
162175
renderComponent();
163176

@@ -166,65 +179,34 @@ describe('ChannelDetailsModal', () => {
166179
});
167180

168181
const closeButton = screen.getByRole('button', { name: /close/i });
169-
expect(closeButton).toBeInTheDocument();
170182
await user.click(closeButton);
171-
});
172-
173-
it('pressing ESC key should close the modal', async () => {
174-
const user = userEvent.setup();
175-
const { container } = renderComponent();
176-
177-
await waitFor(() => {
178-
expect(screen.getByText(testChannel.name)).toBeInTheDocument();
179-
});
180-
181-
expect(container.querySelector('.studio-immersive-modal')).toBeInTheDocument();
182-
183-
await user.keyboard('{Escape}');
184183

185184
await waitFor(() => {
186-
expect(container.querySelector('.studio-immersive-modal')).not.toBeInTheDocument();
185+
expect(screen.queryByTestId('modal-wrapper')).not.toBeInTheDocument();
187186
});
188187
});
189188

190-
it('should display download button after loading', async () => {
189+
it('should close modal when ESC key is pressed', async () => {
190+
const user = userEvent.setup();
191191
renderComponent();
192192

193193
await waitFor(() => {
194-
expect(screen.getByText('Download channel summary')).toBeInTheDocument();
194+
expect(screen.getByTestId('modal-wrapper')).toBeInTheDocument();
195195
});
196-
});
197196

198-
it('should display download button with dropdown functionality', async () => {
199-
const user = userEvent.setup();
200-
renderComponent();
197+
await user.keyboard('{Escape}');
201198

202199
await waitFor(() => {
203-
expect(screen.getByText('Download channel summary')).toBeInTheDocument();
200+
expect(screen.queryByTestId('modal-wrapper')).not.toBeInTheDocument();
204201
});
205-
206-
const downloadButton = screen.getByRole('button', { name: /download channel summary/i });
207-
expect(downloadButton).toBeInTheDocument();
208-
209-
await user.click(downloadButton);
210-
211-
expect(await screen.findByText('Download PDF')).toBeInTheDocument();
212-
expect(await screen.findByText('Download CSV')).toBeInTheDocument();
213202
});
214203

215-
it('should call generateChannelsCSV when CSV option is selected', async () => {
216-
const user = userEvent.setup();
204+
it('should display download button after loading', async () => {
217205
renderComponent();
218206

219207
await waitFor(() => {
220-
expect(screen.getByText(testChannel.name)).toBeInTheDocument();
208+
expect(screen.getByText('Download channel summary')).toBeInTheDocument();
221209
});
222-
223-
const downloadButton = screen.getByRole('button', { name: /download channel summary/i });
224-
await user.click(downloadButton);
225-
226-
const csvOption = await screen.findByText('Download CSV');
227-
await user.click(csvOption);
228210
});
229211

230212
it('should display details panel after loading', async () => {
@@ -250,7 +232,5 @@ describe('ChannelDetailsModal', () => {
250232
await waitFor(() => {
251233
expect(screen.getByText(testChannel.name)).toBeInTheDocument();
252234
});
253-
254-
expect(screen.getByText(testChannel.name)).toBeInTheDocument();
255235
});
256236
});

0 commit comments

Comments
 (0)