Skip to content

Commit 877a1ed

Browse files
bennypowersheyMP
andauthored
feat: modal width attribute (#1738)
* feat: modal width attribute * prevent min-width from overriding small variant * Update CHANGELOG-1.x.md * test: test modal width atts on multiple screen widths * Update CHANGELOG-1.x.md Co-authored-by: Michael Potter <[email protected]>
1 parent 519d16a commit 877a1ed

File tree

10 files changed

+377
-65247
lines changed

10 files changed

+377
-65247
lines changed

CHANGELOG-1.x.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
# 1.11.0 (2021)
22

3-
- [](https://github.com/patternfly/patternfly-elements/commit/) fix: pfe-autocomplete update aria-selected attribute
3+
- [f378caa](https://github.com/patternfly/patternfly-elements/commit/f378caab97b4a2cbf39b9cfad10900bdcf290dde) fix: pfe-autocomplete update aria-selected attribute
44
- [d3ea7fa](https://github.com/patternfly/patternfly-elements/commit/d3ea7facb0c36b7f3e20e2568bdc4bf2e5a5a852) feat: Graceful failure for component registry
55
- [099dc3e](https://github.com/patternfly/patternfly-elements/commit/099dc3e2d3ce732a32ecde0644f5c03ec1e8dd9c) fix: Accordion alignment with latest design kit
66
- [5f88c39](https://github.com/patternfly/patternfly-elements/commit/5f88c3963f8a6c13a9aeba6e9f664678453d46ce) fix: Jump links parseInt for IE11
77
- [43a904e](https://github.com/patternfly/patternfly-elements/commit/43a904e2ce4f2ef7182f803bf35ade463e7c2f1d) fix: container sass placeholder using incorrect variable for spacing (#1522)
8-
- [](https://github.com/patternfly/patternfly-elements/commit/) fix: accordion rendering slotted content in the header
9-
- [](https://github.com/patternfly/patternfly-elements/commit/) feat: vertically center pfe-tab content
8+
- [3d3c4f1](https://github.com/patternfly/patternfly-elements/commit/3d3c4f109a7e09adae3f3f01122846354a278787) fix: accordion rendering slotted content in the header
9+
- [519d16a](https://github.com/patternfly/patternfly-elements/commit/519d16af4c714efdc5246f9c0925ca30447b87ea) feat: vertically center pfe-tab content
10+
- [](https://github.com/patternfly/patternfly-elements/commit/) feat: modal width attribute
11+
1012

1113
# 1.10.1 (2021-07-12)
1214

elements/pfe-modal/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,20 @@
2828
</pfe-cta>
2929
</pfe-modal>
3030
```
31+
32+
## Attributes
33+
The `width` attribute controls the width of the modal window. There are three options: `small`, `medium`, and `large`, the default is `large`.
34+
35+
```html
36+
<pfe-modal width="small">
37+
<button slot="pfe-modal--trigger">Open a small modal</button>
38+
<h2 slot="pfe-modal--header">Modal with a header</h2>
39+
<p>Lorem ipsum dolor sit amet, <a href="#foo">consectetur adipisicing</a> elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
40+
<pfe-cta>
41+
<a href="#bar">Learn more</a>
42+
</pfe-cta>
43+
</pfe-modal>
44+
```
3145
## Slots
3246

3347
### Trigger

elements/pfe-modal/docs/index.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,42 @@ The default slot can contain any type of content. When the header is not present
8383

8484
::: section
8585
## Attributes
86+
87+
### Width
88+
The `width` controls the width of the modal. There are three options: `small`, `medium` and `large`. The default is `large`.
89+
90+
<pfe-modal width="small">
91+
<pfe-button slot="pfe-modal--trigger">
92+
<button>Open a small modal</button>
93+
</pfe-button>
94+
<h2 slot="pfe-modal--header">Small modal with a header</h2>
95+
<p>Lorem ipsum dolor sit amet, <a href="#foo">consectetur adipisicing</a> elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
96+
<pfe-cta>
97+
<a href="#bar">Learn more</a>
98+
</pfe-cta>
99+
</pfe-modal>
100+
101+
<pfe-modal width="medium">
102+
<pfe-button slot="pfe-modal--trigger">
103+
<button>Open a medium modal</button>
104+
</pfe-button>
105+
<h2 slot="pfe-modal--header">Medium modal with a header</h2>
106+
<p>Lorem ipsum dolor sit amet, <a href="#foo">consectetur adipisicing</a> elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
107+
<pfe-cta>
108+
<a href="#bar">Learn more</a>
109+
</pfe-cta>
110+
</pfe-modal>
111+
112+
<pfe-modal width="large">
113+
<pfe-button slot="pfe-modal--trigger">
114+
<button>Open a large modal</button>
115+
</pfe-button>
116+
<h2 slot="pfe-modal--header">Large modal with a header</h2>
117+
<p>Lorem ipsum dolor sit amet, <a href="#foo">consectetur adipisicing</a> elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
118+
<pfe-cta>
119+
<a href="#bar">Learn more</a>
120+
</pfe-cta>
121+
</pfe-modal>
86122
:::
87123

88124
::: section
@@ -131,4 +167,4 @@ detail: {
131167
::: section
132168
## Styling hooks
133169
None
134-
:::
170+
:::

elements/pfe-modal/src/pfe-modal.scss

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ $pfe-modal--breakpoint--medium: 640px;
66

77
$LOCAL-VARIABLES: (
88
context: light,
9+
MaxHeight: 90vh,
910
MaxWidth: 70vw,
10-
MinWidth: 50vw,
1111
MaxWidth--mobile: 94vw,
12-
MaxHeight: 90vh,
12+
MaxWidth--small: #{"min(35rem, 94vw)"},
13+
MaxWidth--medium: #{"min(52.5rem, 94vw)"},
14+
MaxWidth--large: #{"min(70rem, 94vw)"},
15+
MinWidth: 0,
1316
Padding: calc(#{pfe-var(container-padding)} * 2) calc(#{pfe-var(container-padding)} * 3.5) calc(#{pfe-var(container-padding)} * 2) calc(#{pfe-var(container-padding)} * 2),
1417
overlay: (
1518
BackgroundColor: pfe-var(overlay)
@@ -67,9 +70,20 @@ $LOCAL-VARIABLES: (
6770
background-color: pfe-var(surface--lightest);
6871
color: pfe-var(text);
6972
border-radius: pfe-var(ui--border-radius);
70-
73+
7174
@media screen and (min-width: $pfe-modal--breakpoint--medium) {
72-
max-width: pfe-local(MaxWidth);
75+
max-width: var(--pfe-modal-width, pfe-local(MaxWidth--large));
76+
}
77+
:host([width="small"]) & {
78+
max-width: pfe-local(MaxWidth--small);
79+
}
80+
81+
:host([width="medium"]) & {
82+
max-width: pfe-local(MaxWidth--medium);
83+
}
84+
85+
:host([width="large"]) & {
86+
max-width: pfe-local(MaxWidth--large);
7387
}
7488
}
7589
&__container {

elements/pfe-modal/test/index.html

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,9 @@
1111
<script>
1212
// Load and run all tests (.html, .js):
1313
WCT.loadSuites([
14-
'pfe-modal_test.html',
1514
'pfe-modal_react_test.html',
1615
'pfe-modal_vue_test.html',
1716
// @TODO: Deprecate after 1.0
18-
'old-test/pfe-modal_test.html',
1917
'old-test/pfe-modal_react_test.html',
2018
'old-test/pfe-modal_vue_test.html',
2119
]);
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
// Import testing helpers. For more information check out:
2+
// https://open-wc.org/docs/testing/helpers/
3+
import { expect } from '@open-wc/testing/index-no-side-effects.js';
4+
5+
import { setViewport } from '@web/test-runner-commands';
6+
7+
// Import our custom fixture wrapper. This allows us to run tests
8+
// in React and Vue as well as a normal fixture.
9+
import { createFixture } from '../../../test/utils/create-fixture.js';
10+
11+
// Import the element we're testing.
12+
import '../dist/pfe-modal';
13+
14+
// One element, defined here, is used
15+
// in multiple tests. It's torn down and recreated each time.
16+
const testElement =
17+
`<pfe-modal>
18+
</pfe-modal>
19+
`;
20+
21+
const smallModal = `
22+
<pfe-modal width="small">
23+
<pfe-button slot="pfe-modal--trigger">
24+
<button>Open a small modal</button>
25+
</pfe-button>
26+
<h2 slot="pfe-modal--header">Small modal</h2>
27+
<p>Lorem ipsum dolor sit amet, <a href="#foo">consectetur adipisicing</a> elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
28+
</pfe-modal>
29+
`;
30+
31+
const mediumModal = `
32+
<pfe-modal width="medium">
33+
<pfe-button slot="pfe-modal--trigger">
34+
<button>Open a medium modal</button>
35+
</pfe-button>
36+
<h2 slot="pfe-modal--header">Medium modal</h2>
37+
<p>Lorem ipsum dolor sit amet, <a href="#foo">consectetur adipisicing</a> elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
38+
</pfe-modal>
39+
`;
40+
41+
const largeModal = `
42+
<pfe-modal width="large">
43+
<pfe-button slot="pfe-modal--trigger">
44+
<button>Open a large modal</button>
45+
</pfe-button>
46+
<h2 slot="pfe-modal--header">Large modal</h2>
47+
<p>Lorem ipsum dolor sit amet, <a href="#foo">consectetur adipisicing</a> elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
48+
</pfe-modal>
49+
`;
50+
51+
describe("<pfe-modal>", () => {
52+
53+
it("it should upgrade", async () => {
54+
const el = await createFixture(testElement);
55+
56+
expect(el).to.be.an.instanceOf(
57+
customElements.get("pfe-modal"),
58+
'pfe-modal should be an instance of PfeModal'
59+
);
60+
});
61+
62+
// Example test.
63+
it("should apply attributes correctly", async () => {
64+
// Use the same markup that's declared at the top of the file.
65+
const el = await createFixture(`
66+
<pfe-modal>
67+
<pfe-button slot="pfe-modal--trigger">
68+
<button>Open a modal</button>
69+
</pfe-button>
70+
<h2 slot="pfe-modal--header">Modal with a header</h2>
71+
<p>Lorem ipsum dolor sit amet, <a href="#foo">consectetur adipisicing</a> elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
72+
</pfe-modal>
73+
`);
74+
const modalWindow = el.shadowRoot.querySelector('.pfe-modal__window');
75+
const button = el.shadowRoot.querySelector('.pfe-modal__close');
76+
77+
await new Promise(r => setTimeout(r, 25));
78+
expect(el.hasAttribute('has_trigger'), 'has_trigger').to.be.true;
79+
expect(el.hasAttribute('has_header'), 'has_header').to.be.true;
80+
expect(el.hasAttribute('has_body'), 'has_body').to.be.true;
81+
expect(modalWindow.getAttribute('tabindex'), 'modal__window tabindex').to.equal('0');
82+
expect(modalWindow.hasAttribute('hidden'), 'hidden').to.be.true;
83+
expect(button.getAttribute('aria-label'), 'button aria-label').to.equal('Close dialog');
84+
});
85+
86+
it('should open the modal window when the trigger is clicked', async () => {
87+
const el = await createFixture(`
88+
<pfe-modal>
89+
<pfe-button slot="pfe-modal--trigger">
90+
<button>Open a modal</button>
91+
</pfe-button>
92+
<h2 slot="pfe-modal--header">Modal with a header</h2>
93+
<p>Lorem ipsum dolor sit amet, <a href="#foo">consectetur adipisicing</a> elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
94+
</pfe-modal>
95+
`);
96+
const modalWindow = el.shadowRoot.querySelector('.pfe-modal__window');
97+
const button = el.shadowRoot.querySelector('.pfe-modal__close');
98+
const trigger = el.querySelector('[slot=pfe-modal--trigger');
99+
100+
trigger.click();
101+
102+
expect(modalWindow.hasAttribute('hidden')).to.not.be.true;
103+
104+
// reset
105+
button.click();
106+
expect(modalWindow.hasAttribute('hidden')).to.be.true;
107+
});
108+
109+
it('it should remove the hidden attribute from the host on upgrade', async () => {
110+
const el = await createFixture(testElement);
111+
112+
await new Promise(r => setTimeout(r, 25));
113+
// test for the hidden attribute on the host
114+
expect(el.hasAttribute('hidden')).to.not.be.true;
115+
});
116+
117+
describe('on extra large screen', function() {
118+
beforeEach(async function() {
119+
await setViewport({ width: 1600, height: 1200 });
120+
})
121+
122+
describe('with width=small attribute', () => {
123+
it('has small modal width', async () => {
124+
const el = await createFixture(smallModal);
125+
const modalWindow = el.shadowRoot.querySelector('.pfe-modal__window');
126+
expect(getComputedStyle(modalWindow).getPropertyValue('max-width'))
127+
.to.equal('560px');
128+
});
129+
});
130+
131+
describe('with width=medium attribute', () => {
132+
it('has medium modal width', async () => {
133+
const el = await createFixture(mediumModal);
134+
const modalWindow = el.shadowRoot.querySelector('.pfe-modal__window');
135+
expect(getComputedStyle(modalWindow).getPropertyValue('max-width'))
136+
.to.equal('840px');
137+
});
138+
});
139+
140+
describe('with width=large attribute', () => {
141+
it('has large modal width', async () => {
142+
const el = await createFixture(largeModal);
143+
const modalWindow = el.shadowRoot.querySelector('.pfe-modal__window');
144+
expect(getComputedStyle(modalWindow).getPropertyValue('max-width'))
145+
.to.equal('1120px');
146+
});
147+
});
148+
});
149+
150+
describe('on large screen', function() {
151+
beforeEach(async function() {
152+
await setViewport({ width: 1000, height: 800 });
153+
})
154+
155+
describe('with width=small attribute', () => {
156+
it('has small modal width', async () => {
157+
const el = await createFixture(smallModal);
158+
const modalWindow = el.shadowRoot.querySelector('.pfe-modal__window');
159+
expect(getComputedStyle(modalWindow).getPropertyValue('max-width'))
160+
.to.equal('560px');
161+
});
162+
});
163+
164+
describe('with width=medium attribute', () => {
165+
it('has medium modal width', async () => {
166+
const el = await createFixture(mediumModal);
167+
const modalWindow = el.shadowRoot.querySelector('.pfe-modal__window');
168+
expect(getComputedStyle(modalWindow).getPropertyValue('max-width'))
169+
.to.equal('840px');
170+
});
171+
});
172+
173+
describe('with width=large attribute', () => {
174+
it('has large modal width', async () => {
175+
const el = await createFixture(largeModal);
176+
const modalWindow = el.shadowRoot.querySelector('.pfe-modal__window');
177+
expect(getComputedStyle(modalWindow).getPropertyValue('max-width'))
178+
.to.equal('940px');
179+
});
180+
});
181+
});
182+
183+
describe('on medium screen', function() {
184+
beforeEach(async function() {
185+
await setViewport({ width: 768, height: 600 });
186+
})
187+
188+
describe('with width=small attribute', () => {
189+
it('has small modal width', async () => {
190+
const el = await createFixture(smallModal);
191+
const modalWindow = el.shadowRoot.querySelector('.pfe-modal__window');
192+
expect(getComputedStyle(modalWindow).getPropertyValue('max-width'))
193+
.to.equal('560px');
194+
});
195+
});
196+
197+
describe('with width=medium attribute', () => {
198+
it('has medium modal width', async () => {
199+
const el = await createFixture(mediumModal);
200+
const modalWindow = el.shadowRoot.querySelector('.pfe-modal__window');
201+
expect(getComputedStyle(modalWindow).getPropertyValue('max-width'))
202+
.to.equal('721.92px');
203+
});
204+
});
205+
206+
describe('with width=large attribute', () => {
207+
it('has large modal width', async () => {
208+
const el = await createFixture(largeModal);
209+
const modalWindow = el.shadowRoot.querySelector('.pfe-modal__window');
210+
expect(getComputedStyle(modalWindow).getPropertyValue('max-width'))
211+
.to.equal('721.92px');
212+
});
213+
});
214+
});
215+
216+
describe('on small screen', function() {
217+
beforeEach(async function() {
218+
await setViewport({ width: 480, height: 540 });
219+
})
220+
221+
describe('with width=small attribute', () => {
222+
it('has small modal width', async () => {
223+
const el = await createFixture(smallModal);
224+
const modalWindow = el.shadowRoot.querySelector('.pfe-modal__window');
225+
expect(getComputedStyle(modalWindow).getPropertyValue('max-width'))
226+
.to.equal('451.2px');
227+
});
228+
});
229+
230+
describe('with width=medium attribute', () => {
231+
it('has medium modal width', async () => {
232+
const el = await createFixture(mediumModal);
233+
const modalWindow = el.shadowRoot.querySelector('.pfe-modal__window');
234+
expect(getComputedStyle(modalWindow).getPropertyValue('max-width'))
235+
.to.equal('451.2px');
236+
});
237+
});
238+
239+
describe('with width=large attribute', () => {
240+
it('has large modal width', async () => {
241+
const el = await createFixture(largeModal);
242+
const modalWindow = el.shadowRoot.querySelector('.pfe-modal__window');
243+
expect(getComputedStyle(modalWindow).getPropertyValue('max-width'))
244+
.to.equal('451.2px');
245+
});
246+
});
247+
});
248+
});

0 commit comments

Comments
 (0)