Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Space policy UI #883

Merged
merged 31 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
79f7bac
reduce yupdate requirements due to zram
jreidinger Nov 15, 2023
e1df1b7
update documentation
jreidinger Nov 15, 2023
081cfdf
initial attempt for Space policy
jreidinger Nov 15, 2023
d62f91b
WIP: stylesheet reorganization stoled from another branch
ancorgs Nov 29, 2023
b4c92e4
Changes on top of the original SpacePolicy prototype
ancorgs Nov 29, 2023
602016c
[web] Adjust some styles
dgdavid Nov 26, 2023
38902df
SpacePolicy reuses widget for displaying devices
ancorgs Nov 28, 2023
de31d49
Prefer internal components over internal functions
ancorgs Nov 29, 2023
14bb25d
Comments for translators
ancorgs Nov 29, 2023
c7bf43e
Merge branch 'master' into space_policy_ui
joseivanlopez Nov 29, 2023
acdf481
[web] Adjust styles for PF/Hint and PF/ExpandableSection
dgdavid Nov 29, 2023
00c36b9
[web] Change how storage selectors are rendered
dgdavid Nov 29, 2023
08049d6
[web] Structural changes for space policy selector
dgdavid Nov 29, 2023
c6ad047
[web] Reduce the use of "stack" utility CSS class
dgdavid Nov 29, 2023
f786c3c
[web] Adapt texts
joseivanlopez Nov 30, 2023
48ece9f
[web] Always show link to expand affected devices
joseivanlopez Nov 30, 2023
e374fe0
[web] Add tests
joseivanlopez Nov 30, 2023
15b74f7
Adapt overview text based on the storage space policy
ancorgs Nov 30, 2023
f4bd395
Grammar fixes
ancorgs Nov 30, 2023
1d5378c
[web] Small fixes
joseivanlopez Nov 30, 2023
a57a016
[web] Fix test
joseivanlopez Nov 30, 2023
f60f27e
Merge branch 'master' into space_policy_ui
joseivanlopez Nov 30, 2023
15aab26
[web] Adapt selectors to new styles
joseivanlopez Nov 30, 2023
67944b0
[web] Changelog
joseivanlopez Nov 30, 2023
d856e55
[web] Reduce the use of data-type attribute
dgdavid Nov 30, 2023
f50a66c
[web] Reduce the use of grid-template-areas
dgdavid Nov 30, 2023
253aa39
[web] Spacing adjustments
dgdavid Nov 30, 2023
94bb4d4
[web] Drop cursor-pointer CSS utility class
dgdavid Nov 30, 2023
03b0a57
[web] Improve changelog
joseivanlopez Nov 30, 2023
3e306c5
[web] Make code more clear
joseivanlopez Nov 30, 2023
34b8d49
[web] Cover all policy values in switch
joseivanlopez Nov 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.log/
.yardoc/
.vscode/tasks.json
6 changes: 6 additions & 0 deletions web/package/cockpit-agama.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
-------------------------------------------------------------------
Thu Nov 30 15:19:38 UTC 2023 - José Iván López González <jlopez@suse.com>

- Allow selecting the storage policy to make free space
joseivanlopez marked this conversation as resolved.
Show resolved Hide resolved
(gh#openSUSE/agama#883).

-------------------------------------------------------------------
Wed Nov 29 14:15:16 UTC 2023 - José Iván López González <jlopez@suse.com>

Expand Down
133 changes: 81 additions & 52 deletions web/src/assets/styles/blocks.scss
Original file line number Diff line number Diff line change
Expand Up @@ -272,81 +272,110 @@ span.notification-mark[data-variant="sidebar"] {
}
}

.item-list {
[role="option"] {
padding: var(--spacer-normal);
ul[data-type="agama/list"] {
li {
border: 2px solid var(--color-gray-dark);
background: var(--color-gray-light);

border-radius: 5px;
transition: all 0.2s ease-in-out;
display: grid;

gap: var(--spacer-small);

grid-template-columns: 1fr;
grid-template-areas: "content";
padding: var(--spacer-normal);
text-align: start;
background: var(--color-gray-light);
margin-block-end: var(--spacer-small);
}

> [data-type="details"] {
font-size: 80%;
// FIXME: see if it's semantically correct to mark an li as aria-selected when
// not belongs to a listbox or grid list ul.
li[aria-selected] {
border: 2px solid var(--color-primary);
box-shadow: 0 2px 5px 0 var(--color-gray-dark);
background: var(--color-primary);
color: white;
font-weight: 700;

svg {
fill: white;
}
}
}

// These attributes together means that UI is rendering a selector
ul[data-type="agama/list"][role="listbox"] {
li[role="option"] {
transition: all 0.2s ease-in-out;

&:hover {
&:not([aria-selected]) {
background: var(--color-gray-dark);
}
}
}
}

&:is([data-type="locale"]) {
grid-template-columns: 1fr 2fr;
grid-template-areas:
"language territory"
"details details";
// Each kind of list/selector has its way of laying out their items
ul[data-of="agama/storage-devices"] {
li {
display: grid;
gap: var(--spacer-small);
grid-template-columns: 1fr 2fr 2fr;
grid-template-areas: "type-and-size drive-info drive-content";

> [data-type="details"] {
grid-area: details;
}
svg {
vertical-align: inherit;
}

&:is([data-type="timezone"]) {
grid-template-columns: 1fr 2fr 1fr;
grid-template-areas:
"part1 rest-parts time"
"details details .";
> :first-child {
align-self: center;
text-align: center;
justify-self: start;
}
}
}

> [data-type="details"] {
grid-area: details;
}
ul[data-of="agama/space-policies"] {
li *:not(:last-child) {
margin-block-end: var(--spacer-small);
}
}

> [data-type="time"] {
color: var(--color-gray-dimmed);
grid-area: time;
text-align: end;
}
}
ul[data-of="agama/locales"] {
li {
display: grid;
grid-template-columns: 1fr 2fr;
grid-template-areas:
"language territory"
"details details";

&:is([data-type="storage-device"]) {
grid-template-columns: 1fr 2fr 2fr;
grid-template-areas: "type-and-size drive-info drive-content";
> [data-type="details"] {
grid-area: details;
font-size: 80%;
}
}
}

> [data-type="type-and-size"] {
align-self: center;
text-align: center;
justify-self: start;
}
ul[data-of="agama/keymaps"] {
li {
> [data-type="details"] {
font-size: 80%;
}
}
}

[aria-selected] {
border: 2px solid var(--color-primary);
box-shadow: 0 2px 5px 0 var(--color-gray-dark);
background: var(--color-primary);
color: white;
font-weight: 700;
ul[data-of="agama/timezones"] {
li {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
grid-template-areas:
"part1 rest-parts time"
"details details .";

svg {
fill: white;
> [data-type="details"] {
grid-area: details;
font-size: 80%;
}

> [data-type="time"] {
grid-area: time;
color: var(--color-gray-dimmed);
text-align: end;
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions web/src/assets/styles/global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ th {
text-align: start;
}

li {
svg {
vertical-align: middle;
}

span {
margin-inline-start: var(--spacer-small);
}
}

// Style focus making use of :focus-visible
*:focus {
outline: none;
Expand Down
35 changes: 35 additions & 0 deletions web/src/assets/styles/patternfly-overrides.scss
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,38 @@ table td > .pf-v5-c-empty-state {
--pf-v5-c-toggle-group__button--m-selected--BackgroundColor: var(--color-primary);
--pf-v5-c-toggle-group__button--Color: var(--color-gray-light);
}

// Reduce padding of PF/Hint because it looks like an option of current Agama
// select
.pf-v5-c-hint {
--pf-v5-c-hint--PaddingTop: var(--spacer-small);
--pf-v5-c-hint--PaddingRight: var(--spacer-small);
--pf-v5-c-hint--PaddingBottom: var(--spacer-small);
--pf-v5-c-hint--PaddingLeft: var(--spacer-small);
}

// Do not reserve space for PF/Hint actions when there are none
.pf-v5-c-hint__actions:empty {
display: none;
}

// Make PF/ExpandableSection looks a bit different when wrapped in a PF/Hint
.pf-v5-c-hint {
.pf-v5-c-expandable-section {
--pf-v5-c-expandable-section__toggle--Color: var(--color-primary);
}

.pf-v5-c-expandable-section__toggle,
.pf-v5-c-expandable-section__toggle:hover {
// NOTE. would be nice to being able to use darker variant of primary color
// when hovering the link, but we aren't ready yet. We should switch to hsla
// colors or so.
--pf-v5-c-expandable-section__toggle--Color: var(--color-primary);
text-decoration: underline;
}

.pf-v5-c-expandable-section__content {
--pf-v5-c-expandable-section__content--PaddingRight: var(--spacer-normal);
--pf-v5-c-expandable-section__content--PaddingLeft: var(--spacer-normal);
}
}
5 changes: 5 additions & 0 deletions web/src/assets/styles/utilities.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
text-align: center;
}

.title {
font-size: var(--fs-large);
font-weight: var(--fw-bold);
}

.bold {
font-weight: bold;
}
Expand Down
1 change: 1 addition & 0 deletions web/src/assets/styles/variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
--fw-bold: 700;

--fs-base: 14px;
--fs-large: 1rem;
--fs-h1: 1.5rem;
--fs-h2: 1.2rem;

Expand Down
26 changes: 25 additions & 1 deletion web/src/client/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,10 @@ class ProposalManager {
* @property {string} bootDevice
* @property {string} encryptionPassword
* @property {boolean} lvm
* @property {string} spacePolicy
* @property {string[]} systemVGDevices
* @property {Volume[]} volumes
* @property {StorageDevice[]} installationDevices
*
* @typedef {object} Volume
* @property {string} mountPath
Expand Down Expand Up @@ -311,6 +313,8 @@ class ProposalManager {

if (!proxy) return undefined;

const systemDevices = await this.system.getDevices();

const buildResult = (proxy) => {
const buildAction = dbusAction => {
return {
Expand All @@ -320,13 +324,32 @@ class ProposalManager {
};
};

const buildInstallationDevices = (proxy, devices) => {
const findDevice = (devices, name) => {
const device = devices.find(d => d.name === name);

if (device === undefined) console.log("D-Bus object not found: ", name);

return device;
};

const names = proxy.SystemVGDevices.filter(n => n !== proxy.BootDevice).concat([proxy.BootDevice]);
return names.map(dev => findDevice(devices, dev)).filter(Boolean);
joseivanlopez marked this conversation as resolved.
Show resolved Hide resolved
};

return {
settings: {
bootDevice: proxy.BootDevice,
lvm: proxy.LVM,
spacePolicy: proxy.SpacePolicy,
systemVGDevices: proxy.SystemVGDevices,
encryptionPassword: proxy.EncryptionPassword,
volumes: proxy.Volumes.map(this.buildVolume),
// NOTE: strictly speaking, installation devices does not belong to the settings. It
// should be a separate method instead of an attribute in the settings object.
// Nevertheless, it was added here for simplicity and to avoid passing more props in some
// react components. Please, do not use settings as a jumble.
installationDevices: buildInstallationDevices(proxy, systemDevices)
joseivanlopez marked this conversation as resolved.
Show resolved Hide resolved
},
actions: proxy.Actions.map(buildAction)
};
Expand All @@ -341,7 +364,7 @@ class ProposalManager {
* @param {ProposalSettings} settings
* @returns {Promise<number>} 0 on success, 1 on failure
*/
async calculate({ bootDevice, encryptionPassword, lvm, systemVGDevices, volumes }) {
async calculate({ bootDevice, encryptionPassword, lvm, spacePolicy, systemVGDevices, volumes }) {
const dbusVolume = (volume) => {
return removeUndefinedCockpitProperties({
MountPath: { t: "s", v: volume.mountPath },
Expand All @@ -358,6 +381,7 @@ class ProposalManager {
BootDevice: { t: "s", v: bootDevice },
EncryptionPassword: { t: "s", v: encryptionPassword },
LVM: { t: "b", v: lvm },
SpacePolicy: { t: "s", v: spacePolicy },
SystemVGDevices: { t: "as", v: systemVGDevices },
Volumes: { t: "aa{sv}", v: volumes?.map(dbusVolume) }
});
Expand Down
9 changes: 8 additions & 1 deletion web/src/client/storage.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ const contexts = {
LVM: true,
SystemVGDevices: ["/dev/sda", "/dev/sdb"],
EncryptionPassword: "00000",
SpacePolicy: "delete",
Volumes: [
{
MountPath: { t: "s", v: "/" },
Expand Down Expand Up @@ -811,18 +812,20 @@ describe("#proposal", () => {

describe("if there is a proposal", () => {
beforeEach(() => {
contexts.withSystemDevices();
contexts.withProposal();
client = new StorageClient();
});

it("returns the proposal settings and actions", async () => {
const { settings, actions } = await client.proposal.getResult();

expect(settings).toStrictEqual({
expect(settings).toMatchObject({
bootDevice: "/dev/sda",
lvm: true,
systemVGDevices: ["/dev/sda", "/dev/sdb"],
encryptionPassword: "00000",
spacePolicy: "delete",
volumes: [
{
mountPath: "/",
Expand Down Expand Up @@ -861,6 +864,10 @@ describe("#proposal", () => {
]
});

expect(settings.installationDevices.map(d => d.name).sort()).toStrictEqual(
["/dev/sda", "/dev/sdb"].sort()
);

expect(actions).toStrictEqual([
{ text: "Mount /dev/sdb1 as root", subvol: false, delete: false }
]);
Expand Down
8 changes: 6 additions & 2 deletions web/src/components/l10n/KeymapSelector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ import { noop } from "~/utils";
* @typedef {import ("~/clients/l10n").Keymap} Keymap
*/

const ListBox = ({ children, ...props }) => <ul role="listbox" {...props}>{children}</ul>;
const ListBox = ({ children, ...props }) => {
return (
<ul data-type="agama/list" data-of="agama/keymaps" {...props}>{children}</ul>
);
};

const ListBoxItem = ({ isSelected, children, onClick, ...props }) => {
if (isSelected) props['aria-selected'] = true;
Expand Down Expand Up @@ -82,7 +86,7 @@ export default function KeymapSelector({ value, keymaps = [], onChange = noop })
<div className="sticky-top-0">
<ListSearch placeholder={helpSearch} elements={keymaps} onChange={setFilteredKeymaps} />
</div>
<ListBox aria-label={_("Available keymaps")} className="stack item-list">
<ListBox aria-label={_("Available keymaps")} role="listbox">
{ filteredKeymaps.map((keymap, index) => (
<ListBoxItem
key={`keymap-${index}`}
Expand Down
Loading