Skip to content

Commit

Permalink
Adds multi-item view and bulk features
Browse files Browse the repository at this point in the history
  • Loading branch information
martinlingstuyl committed Sep 18, 2024
1 parent 13dad07 commit 7fec2ff
Show file tree
Hide file tree
Showing 30 changed files with 2,160 additions and 621 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,29 @@ Using this solution you can:
- view retention information from files and folders labeled with a retention label.
- toggle record lock status
- clear the retention label from a file or folder.
- bulk toggle record status for selected items or the entire library.
- bulk clear labels for selected items or the entire library.

This solution comes in handy if you choose to not publish any retention label, for example for purposes of automatic labelling. In such a scenario, the retention label dropdown would not be visible, and thus it would be impossible to clear the label of an item.

Opening the Retention Controls dialog:

![Opening the retention controls screen](screenshot_1.jpg)

Viewing retention information for a single selected item:

![The retention controls screen](screenshot_2.jpg)

Viewing retention information for multiple selected items or the entire library:

![Retention controls for the entire library](screenshot_3.jpg)

Taking action on a single item, all selected items or the entire library:

![Executing actions](screenshot_4.jpg)

> "Take bulk action" encompasses the entire library if no items were selected before opening the retention controls dialog.
## Used SharePoint Framework Version

![version](https://img.shields.io/badge/version-1.19.0-green.svg)
Expand Down
2 changes: 1 addition & 1 deletion config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
"localizedResources": {
"RetentionControlsCommandSetStrings": "lib/extensions/retentionControls/loc/{locale}.js"
}
}
}
2 changes: 1 addition & 1 deletion config/package-solution.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"solution": {
"name": "Blimped SPFx Retention Controls Client Side Solution",
"id": "39608f99-117d-457c-9001-79c8d257aab5",
"version": "1.0.8.1",
"version": "1.1.0.1",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"isDomainIsolated": false,
Expand Down
109 changes: 62 additions & 47 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
},
"dependencies": {
"@fluentui/react": "8.118.9",
"@fluentui/react-file-type-icons": "8.11.21",
"@microsoft/decorators": "1.19.0",
"@microsoft/sp-core-library": "1.19.0",
"@microsoft/sp-dialog": "1.19.0",
Expand Down
Binary file added screenshot_3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshot_4.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
55 changes: 55 additions & 0 deletions src/extensions/retentionControls/AlertDialogManager.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import * as ReactDOM from "react-dom";
import * as React from "react";
import Dialog, { DialogFooter, DialogType } from "@fluentui/react/lib/Dialog";
import { DefaultButton } from "@fluentui/react/lib/Button";
import * as strings from "RetentionControlsCommandSetStrings";

export default class AlertDialogManager {
private domElement: HTMLDivElement | null = null;
private onClosedCallback: (confirmed?: boolean) => void;

constructor(private title: string, private subText: string, private buttonText?: string) {
}

public async close(confirmed?: boolean): Promise<void> {
if (this.domElement) {
ReactDOM.unmountComponentAtNode(this.domElement);
this.domElement.remove();
this.domElement = null;
}

if (this.onClosedCallback !== undefined) {
this.onClosedCallback(confirmed);
}
}

public onClosed(callback: (confirmed?:boolean)=>void): void {
this.onClosedCallback = callback;
}

public async show(): Promise<void> {
this.domElement = document.createElement('div');
document.body.appendChild(this.domElement);

const close = async (confirmed?: boolean): Promise<void> => {
await this.close(confirmed);
};

const reactElement = <Dialog
hidden={false}
onDismiss={() => close()}
dialogContentProps={{
type: DialogType.normal,
showCloseButton: false,
title: this.title,
subText: this.subText,
}}>
<DialogFooter>
{ <DefaultButton onClick={() => close(false)} text={this.buttonText ?? strings.CloseModal} /> }
</DialogFooter>
</Dialog>

ReactDOM.render(reactElement,
this.domElement);
}
}
57 changes: 57 additions & 0 deletions src/extensions/retentionControls/ConfirmationDialogManager.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import * as ReactDOM from "react-dom";
import * as React from "react";
import Dialog, { DialogFooter, DialogType } from "@fluentui/react/lib/Dialog";
import { DefaultButton, PrimaryButton } from "@fluentui/react/lib/Button";
import * as strings from "RetentionControlsCommandSetStrings";

export default class ConfirmationDialogManager {
private domElement: HTMLDivElement | null = null;
private onClosedCallback: (confirmed?: boolean) => void;

constructor(private title: string, private subText: string, private primaryButtonText?: string, private secondaryButtonText?: string, private showPrimaryButton: boolean = true, private showSecondaryButton: boolean = true) {
}

public async close(confirmed?: boolean): Promise<void> {
if (this.domElement) {
ReactDOM.unmountComponentAtNode(this.domElement);
this.domElement.remove();
this.domElement = null;
}

if (this.onClosedCallback !== undefined) {
this.onClosedCallback(confirmed);
}
}

public onClosed(callback: (confirmed?:boolean)=>void): void {
this.onClosedCallback = callback;
}

public async show(): Promise<void> {
this.domElement = document.createElement('div');
document.body.appendChild(this.domElement);

const close = async (confirmed?: boolean): Promise<void> => {
await this.close(confirmed);
};

const reactElement = <Dialog
hidden={false}
onDismiss={() => close()}
dialogContentProps={{
type: DialogType.normal,
showCloseButton: true,
title: this.title,
closeButtonAriaLabel: 'Close',
subText: this.subText,
}}>
<DialogFooter>
{ this.showPrimaryButton ? <PrimaryButton onClick={() => close(true)} text={this.primaryButtonText ?? strings.Yes} /> : <></> }
{ this.showSecondaryButton ? <DefaultButton onClick={() => close(false)} text={this.secondaryButtonText ?? strings.No} /> : <></> }
</DialogFooter>
</Dialog>

ReactDOM.render(reactElement,
this.domElement);
}
}
Loading

0 comments on commit 7fec2ff

Please sign in to comment.