-
Notifications
You must be signed in to change notification settings - Fork 44
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
Storage: show skeletons only when needed #1171
Conversation
Also display progress for loading the installation device
I see the form for selecting the installation device is now protected from "quick clickers"[1]. But that's the only pop-up that got that protection. What about these?
[1] Although I don't like using a skeleton within the pop-up instead of some kind of "spinner", but that's another topic for another comment. |
</Form> | ||
<If | ||
condition={isLoading} | ||
then={ <SectionSkeleton numRows={6} /> } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, this skeleton here looks really weird.
As commented during the planning meeting of the card and again today during review, the goal of the skeletons is to "reserve space" in the UI for the future content, so things don't jump up and down as that content is loaded into its place.
In case of a pop-up that is going to load at once as soon the content arrives, that multi-line skeleton looks like some kind of render error. I expected the pop-up to open kind of blank but with some "loading" message and/or a spinner (I personally expected the same three circles we have when loading the whole UI).
// it does not depend on any changed item and does not show skeleton later. | ||
// the ProposalResultSection is refreshed always | ||
InstallationDeviceField: [CHANGING.ENCRYPTION, CHANGING.BOOT, CHANGING.POLICY, CHANGING.VOLUMES], | ||
PartitionsField: [CHANGING.ENCRYPTION], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this include CHANGING.POLICY
? I don't see how changing the find space policy can affect the list of volumes.
// the ProposalResultSection is refreshed always | ||
InstallationDeviceField: [CHANGING.ENCRYPTION, CHANGING.BOOT, CHANGING.POLICY, CHANGING.VOLUMES], | ||
PartitionsField: [CHANGING.ENCRYPTION], | ||
SpacePolicyField: [CHANGING.ENCRYPTION, CHANGING.BOOT, CHANGING.VOLUMES, CHANGING.TARGET], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a comment about this. Being fully strict, the fact is that the content of the sentence displayed right after "Find Space" can actually be affected by changes in the target disk(s), in the booting disk and in the list of volumes.
For example, the sentence can initially be "deleting all content of the installation disk". But then I change the target and decide to install on an LVM over sdb and sdc. Then the sentence changes to "deleting all content of the 2 selected disks". Then I decide that, in addition to that, the system will boot from sda so the sentence changes to "deleting all content of the 3 selected disks". And for extra fun I decide to allocate /home as a new partition at sdd. Then the sentence changes again to "deleting all content of the 4 selected disks".
I don't know if those changes in the sentence really deserve a skeleton. I just wanted to mention there is SOME influence and as such it should be documented next to the code.
In fact, Watching it become a skeleton so often feels quite wrong. |
web/src/components/core/If.jsx
Outdated
return condition ? positive : negative; | ||
const ret = condition ? positive : negative; | ||
// if the parameter is a function then evaluate it | ||
return (typeof ret === "function") ? ret() : ret; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This allows lazy evaluation of the branches. The problem is that <If>
evaluates both branches and then returns one of them.
So for example this
<If condition={data === undefined} then={<Loading/>} else={<Foo foo={data.foo}>} />
crashes when data
is undefined
because it evaluates the else
branch too early.
With this improvement you can pass a function which can be evaluated only when the result is needed. (Passing functions is quite common in Javascript to allow the lazy evaluation.)
Then the fix is trivial, just add () =>
before the value:
<If condition={data === undefined} then={<Loading/>} else={() => <Foo foo={data.foo}>} />
@dgdavid are you OK with this change?
I'll add some example into the <If>
documentation if you agree with this improvement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @lslezak
Let's say that I have no strong opinion about it. At first sight, it looks reasonable to prevent not needed evaluations. But on the other hand it's weird to me to accept a function as content of the branches. IMHO it should be limited to ReactNode (Although technically a component is a function too).
That said, I fully understand the proposal looking at the code written at https://github.com/openSUSE/agama/pull/1171/files#diff-994f1f4ff00fecec1797a2aaa799dbbeddc4e8fb2c1ae666fedbb95b7c499770R155-R178
<If
condition={isLoading}
then={
<Loading text={_("Loading data...")} />
}
else={
() =>
<Form id="space-policy-form" onSubmit={onSubmit}>
<SpacePolicyPicker currentPolicy={policy} onChange={setPolicy} />
<If
condition={devices.length > 0}
then={
<SpaceActionsTable
devices={devices}
expandedDevices={expandedDevices}
deviceAction={deviceAction}
isActionDisabled={policy.id !== "custom"}
onActionChange={changeActions}
/>
}
/>
</Form>
}
/>
But I would go for a different implementation instead. For me, what is in the else should be a component that React should be able to understand. Let's say <SpacePolicyForm />
. Then, the component should be aware about what it needs for return either, an output or nothing.
const SpacePolicyForm = ({ prop1, prop2 }) => {
if (!prop1 || !prop2) return;
...
return (
<div><b>{prop1}:</b>{prop2}</div>
);
}
I.e., the key for me is to write more robust components.
Of course, I might be wrong, so maybe another view here could help to decide what way to go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was assuming the else
branch is not evaluated if the condition is true. So, probably, I have written unsafe code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, as far as I can see I think that this set of changes reveals another change we could address: to add the isLoading
prop to core/Popup and make it show the spinner or its children based on the prop value. That way, we could reduce the <If condition={isLoading} then={...
repetition and have more clean WhateverDialog.jsx components.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After reading this I decided to revert the <If>
change. It seems it is not safe and it can have some nasty side effects in some cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@joseivanlopez Actually I do not think you have written "unsafe" code. The Form
itself is not executed (@dgdavid and me has done some tests with another example). The problem is the data.foo
when data
is undefined (that's the code that gets executed).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks.
// a PF skeleton is displayed | ||
expect(container.querySelector("div.pf-v5-c-skeleton")).toBeVisible(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NP: We use to mock the PF/Skeleton and look for the mocked content instead, which is somehow more aligned with RTL principles (to look for what the user see, not how it's styled).
But feel free to keep the test as it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's also more robust in the sense that no matter if in the next PF update they change the HTML node for wrapping the content or the CSS classes used for styling it. The latest will happens as soon as we migrate to PFv6.
Prepare for releasing Agama 8. It includes the following pull requests: * #884 * #886 * #914 * #918 * #956 * #957 * #958 * #959 * #960 * #961 * #962 * #963 * #964 * #965 * #966 * #969 * #970 * #976 * #977 * #978 * #979 * #980 * #981 * #983 * #984 * #985 * #986 * #988 * #991 * #992 * #995 * #996 * #997 * #999 * #1003 * #1004 * #1006 * #1007 * #1008 * #1009 * #1010 * #1011 * #1012 * #1014 * #1015 * #1016 * #1017 * #1020 * #1022 * #1023 * #1024 * #1025 * #1027 * #1028 * #1029 * #1030 * #1031 * #1032 * #1033 * #1034 * #1035 * #1036 * #1038 * #1039 * #1041 * #1042 * #1043 * #1045 * #1046 * #1047 * #1048 * #1052 * #1054 * #1056 * #1057 * #1060 * #1061 * #1062 * #1063 * #1064 * #1066 * #1067 * #1068 * #1069 * #1071 * #1072 * #1073 * #1074 * #1075 * #1079 * #1080 * #1081 * #1082 * #1085 * #1086 * #1087 * #1088 * #1089 * #1090 * #1091 * #1092 * #1093 * #1094 * #1095 * #1096 * #1097 * #1098 * #1099 * #1100 * #1102 * #1103 * #1104 * #1105 * #1106 * #1109 * #1110 * #1111 * #1112 * #1114 * #1116 * #1117 * #1118 * #1119 * #1120 * #1121 * #1122 * #1123 * #1125 * #1126 * #1127 * #1128 * #1129 * #1130 * #1131 * #1132 * #1133 * #1134 * #1135 * #1136 * #1138 * #1139 * #1140 * #1141 * #1142 * #1143 * #1144 * #1145 * #1146 * #1147 * #1148 * #1149 * #1151 * #1152 * #1153 * #1154 * #1155 * #1156 * #1157 * #1158 * #1160 * #1161 * #1162 * #1163 * #1164 * #1165 * #1166 * #1167 * #1168 * #1169 * #1170 * #1171 * #1172 * #1173 * #1174 * #1175 * #1177 * #1178 * #1180 * #1181 * #1182 * #1183 * #1184 * #1185 * #1187 * #1188 * #1189 * #1190 * #1191 * #1192 * #1193 * #1194 * #1195 * #1196 * #1198 * #1199 * #1200 * #1201 * #1203 * #1204 * #1205 * #1206 * #1207 * #1208 * #1209 * #1210 * #1211 * #1212 * #1213 * #1214 * #1215 * #1216 * #1217 * #1219 * #1220 * #1221 * #1222 * #1223 * #1224 * #1225 * #1226 * #1227 * #1229
Problem
Solution
Loading Skeletons
Installation Device Progress
undefined
screenreaderText Properties
screenreaderText
properties for skeletons and keep it only for the result sectionQuestions
Testing
Screenshots
When enabling/disabling the Btrfs snapshots all data was refreshed, now the installation device and the space policy does not show the progress skeletons.
Another fix was empty installation device screen when the "Installation device" link was clicked quickly, before loading the data finished.