Skip to content

Commit

Permalink
[web] Move LVM setting to storage device section
Browse files Browse the repository at this point in the history
Since, using @ancorgs's words, it is more

  > consistent with the future we want to go (in which LVM is part of
  > the device selection)
  • Loading branch information
dgdavid committed Feb 22, 2024
1 parent 93c4d21 commit ba0114d
Show file tree
Hide file tree
Showing 4 changed files with 499 additions and 497 deletions.
222 changes: 220 additions & 2 deletions web/src/components/storage/ProposalDeviceSection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,19 @@
*/

import React, { useState } from "react";
import { Button, Form, Skeleton } from "@patternfly/react-core";
import {
Button,
Form,
Skeleton,
Switch,
ToggleGroup, ToggleGroupItem,
Tooltip
} from "@patternfly/react-core";

import { _ } from "~/i18n";
import { Icon } from "~/components/layout";
import { If, Section, Popup } from "~/components/core";
import { DeviceSelector } from "~/components/storage";
import { DeviceList, DeviceSelector } from "~/components/storage";
import { deviceLabel } from '~/components/storage/utils';
import { noop } from "~/utils";

Expand Down Expand Up @@ -167,6 +175,201 @@ const InstallationDeviceField = ({
);
};

/**
* Form for configuring the system volume group.
* @component
*
* @param {object} props
* @param {string} props.id - Form ID.
* @param {ProposalSettings} props.settings - Settings used for calculating a proposal.
* @param {StorageDevice[]} [props.devices=[]] - Available storage devices.
* @param {onSubmitFn} [props.onSubmit=noop] - On submit callback.
* @param {onValidateFn} [props.onValidate=noop] - On validate callback.
*
* @callback onSubmitFn
* @param {string[]} devices - Name of the selected devices.
*
* @callback onValidateFn
* @param {boolean} valid
*/
const LVMSettingsForm = ({
id,
settings,
devices = [],
onSubmit: onSubmitProp = noop,
onValidate = noop
}) => {
const [vgDevices, setVgDevices] = useState(settings.systemVGDevices);
const [isBootDeviceSelected, setIsBootDeviceSelected] = useState(settings.systemVGDevices.length === 0);
const [editedDevices, setEditedDevices] = useState(false);

const selectBootDevice = () => {
setIsBootDeviceSelected(true);
onValidate(true);
};

const selectCustomDevices = () => {
setIsBootDeviceSelected(false);
const { bootDevice } = settings;
const customDevices = (vgDevices.length === 0 && !editedDevices) ? [bootDevice] : vgDevices;
setVgDevices(customDevices);
onValidate(customDevices.length > 0);
};

const onChangeDevices = (selection) => {
const selectedDevices = devices.filter(d => selection.includes(d.sid)).map(d => d.name);
setVgDevices(selectedDevices);
setEditedDevices(true);
onValidate(devices.length > 0);
};

const onSubmit = (e) => {
e.preventDefault();
const customDevices = isBootDeviceSelected ? [] : vgDevices;
onSubmitProp(customDevices);
};

const BootDevice = () => {
const bootDevice = devices.find(d => d.name === settings.bootDevice);

// FIXME: In this case, should be a "readOnly" selector.
return <DeviceList devices={[bootDevice]} />;
};

return (
<Form id={id} onSubmit={onSubmit}>
<div className="split">
<span>{_("Devices for creating the volume group")}</span>
<ToggleGroup isCompact>
<ToggleGroupItem
text={_("Installation device")}
buttonId="bootDevice"
isSelected={isBootDeviceSelected}
onClick={selectBootDevice}
/>
<ToggleGroupItem
text={_("Custom devices")}
buttonId="customDevices"
isSelected={!isBootDeviceSelected}
onClick={selectCustomDevices}
/>
</ToggleGroup>
</div>
<If
condition={isBootDeviceSelected}
then={<BootDevice />}
else={
<DeviceSelector
isMultiple
selected={devices.filter(d => vgDevices?.includes(d.name))}
devices={devices}
onChange={onChangeDevices}
/>
}
/>
</Form>
);
};

/**
* Allows to select LVM and configure the system volume group.
* @component
*
* @param {object} props
* @param {ProposalSettings} props.settings - Settings used for calculating a proposal.
* @param {StorageDevice[]} [props.devices=[]] - Available storage devices.
* @param {boolean} [props.isChecked=false] - Whether LVM is selected.
* @param {boolean} [props.isLoading=false] - Whether to show the selector as loading.
* @param {onChangeFn} [props.onChange=noop] - On change callback.
*
* @callback onChangeFn
* @param {boolean} lvm
*/
const LVMField = ({
settings,
devices = [],
isChecked: isCheckedProp = false,
isLoading = false,
onChange: onChangeProp = noop
}) => {
const [isChecked, setIsChecked] = useState(isCheckedProp);
const [isFormOpen, setIsFormOpen] = useState(false);
const [isFormValid, setIsFormValid] = useState(true);

const onChange = (_, value) => {
setIsChecked(value);
onChangeProp({ lvm: value, vgDevices: [] });
};

const openForm = () => setIsFormOpen(true);

const closeForm = () => setIsFormOpen(false);

const onValidateForm = (valid) => setIsFormValid(valid);

const onSubmitForm = (vgDevices) => {
closeForm();
onChangeProp({ vgDevices });
};

const description = _("Configuration of the system volume group. All the file systems will be \
created in a logical volume of the system volume group.");

const LVMSettingsButton = () => {
return (
<Tooltip
content={_("Configure the LVM settings")}
entryDelay={400}
exitDelay={50}
position="right"
>
<button aria-label={_("LVM settings")} className="plain-control" onClick={openForm}>
<Icon name="tune" size="s" />
</button>
</Tooltip>
);
};

if (isLoading) return <Skeleton width="25%" />;

return (
<div className="split">
<Switch
id="lvm"
label={_("Use logical volume management (LVM)")}
isReversed
isChecked={isChecked}
onChange={onChange}
/>
<If condition={isChecked} then={<LVMSettingsButton />} />
<Popup
aria-label={_("LVM settings")}
title={_("System Volume Group")}
description={description}
isOpen={isFormOpen}
>
<LVMSettingsForm
id="lvmSettingsForm"
devices={devices}
settings={settings}
onSubmit={onSubmitForm}
onValidate={onValidateForm}
/>
<Popup.Actions>
<Popup.Confirm
form="lvmSettingsForm"
type="submit"
isDisabled={!isFormValid}
>
{_("Accept")}
</Popup.Confirm>
<Popup.Cancel onClick={closeForm} />
</Popup.Actions>
</Popup>
</div>
);
};

/**
* Section for editing the selected device
* @component
Expand Down Expand Up @@ -195,6 +398,14 @@ export default function ProposalDeviceSection({
}
};

const changeLVM = ({ lvm, vgDevices }) => {
const settings = {};
if (lvm !== undefined) settings.lvm = lvm;
if (vgDevices !== undefined) settings.systemVGDevices = vgDevices;

onChange(settings);
};

const Description = () => (
<span
dangerouslySetInnerHTML={{
Expand All @@ -217,6 +428,13 @@ export default function ProposalDeviceSection({
isLoading={isLoading && bootDevice === undefined}
onChange={changeBootDevice}
/>
<LVMField
settings={settings}
devices={availableDevices}
isChecked={settings.lvm === true}
isLoading={settings.lvm === undefined}
onChange={changeLVM}
/>
</Section>
);
}
Loading

0 comments on commit ba0114d

Please sign in to comment.