In a recent project I completed, our client wanted to have some nicely designed dashboards on the homepage of their portal so they could keep track of project updates and costs. We looked at a few options but ultimately, they wanted simplicity (ease of use) and a nice look and feel.
You have a few options when it comes to building additional functionality for SharePoint beyond the existing web parts on offer.
SPFX Webparts
Pros
Full Integration: SPFx web parts integrate fully with SharePoint capabilities, including support for modern user interfaces and access to the full functionality of the SharePoint platform.
Performance: Being client-side, SPFx web parts run in the context of the current user and browser, leading to faster interactions and data processing compared to server-side components.
Modern Tooling: Utilizes popular JavaScript frameworks and tools such as React, Angular, or Vue.js, allowing developers to use the latest web development practices and libraries.
Cons
Complex Setup: Requires a solid understanding of web development frameworks, npm packages, and the overall SharePoint Framework environment.
Development Overhead: Developing SPFx web parts demands more upfront time and effort, especially when dealing with setup, compilation, and debugging.
Resource Intensive: Since these are client-side web parts, they can be resource-intensive on the client's machine, especially if not optimized properly.
Canvas Apps
Pros
User-Friendly Builder: The intuitive design of the Canvas App builder allows non-developers to create powerful apps with little to no coding.
Rapid Development: Quickly prototype and deploy apps that interact with SharePoint data and other Microsoft services.
Customisable UI: Design a user interface that matches your specific business needs and workflows.
Cons
Embedding Issues: When embedded in SharePoint pages, Canvas Apps can appear as if they are isolated, similar to being in an iframe, which might not seamlessly integrate with the page’s design.
Performance Concerns: Canvas Apps need to load within the SharePoint environment, which can result in slower load times and potentially impact user experience.
SharePoint Lists
Pros
Easy to Use and Access: SharePoint Lists are straightforward to set up and utilize, making them accessible to users with varying levels of technical expertise.
Highly Customizable: With JSON, users can customise views, forms, and column formatting to enhance the visual appeal and functionality of the lists.
Integrated SharePoint Experience: Being a native feature, lists work seamlessly within the SharePoint environment and have a nicer embedding experience.
Cons
JSON Knowledge Required: To effectively customise the lists, users need to understand JSON, which can be a barrier for those without technical skills.
Limited Functionality: While JSON allows for considerable customisation of appearance and some behavior, it does not permit the same level of interaction or complex logic that custom web parts or Canvas Apps provide.
Dependent on SharePoint UI: The customisation is dependent on the SharePoint UI constraints and may not always meet all specific user needs or expectations.
Building my SharePoint List
For this project I decided to use SharePoint list formatting. I wanted a simple embedded experience that looked like it was part of the site rather than a standalone app.
For my client, the list was larger and more complicated than the below, but these were the type of fields I needed to use from the list so for this example I am just going to talk about these. The Programme Start and Programme end were set with default values so that when a user adds a new project to the list, they don't need to set these.
ProgrammeStart - Date with default value set
ProgrammeEnd - Date with default value set
ProjectStart - Date
ProjectEnd - Date
You could take these and change them to project and tasks if you wanted to go down a level.
Next, I needed to have this display as a timeline showing the progress of the project over time.
When styling SP lists, you will need to use SharePoint safe colours too. You can get these from here - SharePoint themes and colors | Microsoft Learn
The JSON Formatting
I'll paste each part of the JSON below with comments, you will need to remove the comments if using this in your solution. This section of the JSON configuration is dedicated to setting up the overall appearance and behavior of rows in a SharePoint (SP) list. It defines the schema for validation, opts to simplify the interface by hiding selection options and column headers, and uses CSS styling within a flexible box (flexbox) model to organise the content within each row efficiently and cleanly.
{
"$schema": "https://developer.microsoft.com/json-schemas/sp/v2/row-formatting.schema.json",
// Sets the JSON schema for SharePoint row formatting to validate the structure and values
"hideSelection": true,
// Option to hide the row selection feature, enhancing visual cleanliness when not needed
"hideColumnHeader": true,
// Option to hide column headers, useful for customized display or when headers are not necessary
"rowFormatter": {
"elmType": "div",
// Defines a 'div' element as the container for each row
"style": {
"display": "flex",
// Use flexbox layout to align children elements flexibly
"flex-direction": "column",
// Arrange child elements in a vertical stack
"align-items": "flex-start",
// Align child elements to the start of the flex alignment container
"height": "60px",
// Set a fixed height for each row
"padding": "8px",
// Add padding inside the row for spacing around content
"box-sizing": "border-box"
// Include padding and border in the element's total width and height
},
In this section, the configuration continues to detail the structure and styling within the rows of a SharePoint list. It introduces a nested div arrangement for managing specific content, starting with the primary title of each list item. The style rules ensure that the title is prominently displayed, with measures to handle overflow and maintain a clean and organised presentation. The use of flexbox continues to offer a flexible and responsive layout configuration.
"children": [
{
"elmType": "div",
// Defines a 'div' element to structure and containerize the row content
"style": {
"display": "flex",
// Utilizes flexbox layout to align children elements horizontally
"flex-direction": "row",
// Set the direction of the flex items in the container to a row (horizontal alignment)
"align-items": "center",
// Aligns child elements at the center of the row vertically
"width": "100%"
// Ensure the div takes the full width of its container
},
"children": [
{
"elmType": "div",
// Create another 'div' to encapsulate the title content specifically
"txtContent": "[$Title]",
// Dynamically inserts the 'Title' value from the SharePoint list item into the div
"style": {
"width": "200px",
// Assigns a fixed width to the title container
"padding-right": "20px",
// Adds right padding to separate the title from following content
"font-weight": "bold",
// Makes the title font bold for emphasis
"overflow": "hidden",
// Prevents the content from overflowing its container
"text-overflow": "ellipsis",
// Adds an ellipsis if the title is too long to fit
"white-space": "nowrap"
// Keeps the text on a single line and prevents it from wrapping
}
},
This segment extends the visual customisation of list items to represent project timelines dynamically. It uses nested div elements styled to show progress based on calculated time frames and conditions. The style and functionality are designed to adaptively reflect the current status of projects relative to their planned timelines, providing a clear and informative visual representation.
{
"elmType": "div",
// A container div that represents a background bar, often used for visualizing timelines or progress
"style": {
"position": "relative",
// Sets position to relative to allow absolute positioning of child elements within this div
"flex-grow": 1,
// Allows the div to grow and take up any available space in the flex container
"height": "20px",
// Specifies a fixed height for the progress bar
"background-color": "#E0E0E0",
// Sets a light grey background color as a base for the progress indicator
"border-radius": "5px"
// Rounds the corners of the bar for a smoother visual appearance
},
"children": [
{
"elmType": "div",
// A child div that shows the progress made on a project
"style": {
"position": "absolute",
// Positions the div absolutely within its parent container
"left": "=if([$ProjectStart] == '', ((Date(@now) - Date([$ProgrammeStart])) / (Date([$ProgrammeEnd]) - Date([$ProgrammeStart])) * 100) + '%', ((Date([$ProjectStart]) - Date([$ProgrammeStart])) / (Date([$ProgrammeEnd]) - Date([$ProgrammeStart])) * 100) + '%')",
// Dynamically sets the left position based on the project's start date relative to the programme's timeline
"background-color": "#40c5af",
// Sets a distinctive teal color for the progress portion of the bar
"height": "100%",
// Matches the height of the parent container to maintain a consistent bar thickness
"border-radius": "=if([$ProjectStart] == '' || [$ProjectEnd] == '', '0px', if(Date(@now) >= Date([$ProjectEnd]), '5px 5px 5px 5px', '5px 0px 0px 5px'))",
// Conditionally adjusts the border-radius based on whether the project start or end dates are set
"width": "=if([$ProjectStart] == '', '0%', if([$ProjectEnd] == '', ((Date(@now) - Date([$ProjectStart])) / (Date([$ProgrammeEnd]) - Date([$ProgrammeStart])) * 100) + '%', if(Date(@now) < Date([$ProjectEnd]), ((Date(@now) - Date([$ProjectStart])) / (Date([$ProgrammeEnd]) - Date([$ProgrammeStart])) * 100) + '%', ((Date([$ProjectEnd]) - Date([$ProjectStart])) / (Date([$ProgrammeEnd]) - Date([$ProgrammeStart])) * 100) + '%')))",
// Calculates the width of the progress bar based on the elapsed duration of the project relative to its total duration
"display": "=if([$ProjectStart] == '' && [$ProjectEnd] == '', 'none', 'block')"
// Controls the display of the progress bar, hiding it if there are no start or end dates
}
},
{
"elmType": "div",
// A second div to visually indicate the remaining duration or to highlight another aspect of the timeline
"style": {
"position": "absolute",
// Also absolutely positioned within the same parent
"left": "=if([$ProjectEnd] == '', ((Date(@now) - Date([$ProgrammeStart])) / (Date([$ProgrammeEnd]) - Date([$ProgrammeStart])) * 100) + '%', if(Date(@now) > Date([$ProjectEnd]), ((Date([$ProjectEnd]) - Date([$ProgrammeStart])) / (Date([$ProgrammeEnd]) - Date([$ProgrammeStart])) * 100) + '%', ((Date(@now) - Date([$ProgrammeStart])) / (Date([$ProgrammeEnd]) - Date([$ProgrammeStart])) * 100) + '%'))",
// Sets the left position based on the project end date or the current date
"background-color": "#b2b2b2",
// Uses a darker shade of grey to differentiate this segment from the progress part
"height": "100%",
// Ensures it matches the parent's height for uniformity
"border-radius": "0px 5px 5px 0px",
// Applies rounded corners to the right side only, creating a finished look on the right edge
"width": "=if([$ProjectEnd] == '', '0%', if(Date(@now) < Date([$ProjectEnd]), ((Date([$ProjectEnd]) - Date(@now)) / (Date([$ProgrammeEnd]) - Date([$ProgrammeStart])) * 100) + '%', '0%'))",
// Calculates the width based on remaining time to project end, shrinking as time progresses
"display": "=if([$ProjectStart] == '' || [$ProjectEnd] == '', 'none', 'block')"
// Ensures this part of the bar is only displayed if relevant dates are available
}
}
]
},
This section enhances the visual display of a project's timeline by adding key dates as reference points. It uses absolute positioning to place each date correctly along a horizontal timeline, from the project's start and end dates to predefined boundary dates for the overall period. The dynamic text content for project start and end dates is rendered based on available data, providing an interactive and informative timeline visualisation that adjusts based on actual project data. This helps viewers quickly understand the temporal context of the project's lifecycle relative to the overall timeline.
{
"elmType": "div",
// A 'div' element to display a static date indicating the start of a timeline or range
"txtContent": "01/01/2018",
// Static text content, representing a fixed start date for visualization purposes
"style": {
"position": "absolute",
// Uses absolute positioning to place the text at a specific location
"left": "0px",
// Positions the text at the very beginning of the horizontal line
"top": "-20px",
// Positions the text 20 pixels above the parent container
"color": "#555",
// Sets text color to a medium gray for readability
"font-size": "10px"
// Specifies a smaller font size for subtlety and space efficiency
}
},
{
"elmType": "div",
// A 'div' element to dynamically display the project's start date
"txtContent": "=toLocaleDateString([$ProjectStart])",
// Converts the project start date from SharePoint list to a localized string format
"style": {
"position": "absolute",
// Absolute positioning to place the date precisely
"left": "=if([$ProjectStart] == '', '0%', ((Date([$ProjectStart]) - Date([$ProgrammeStart])) / (Date([$ProgrammeEnd]) - Date([$ProgrammeStart])) * 100 -8) + '%')",
// Calculates position based on the start date's proportion of the total timeline, adjusted slightly to the left
"top": "2px",
// Slightly offsets the text below the top line of its container
"color": "#555",
// Uses medium gray color for the text
"font-size": "10px"
// Keeps the font small to avoid cluttering the visual representation
}
},
{
"elmType": "div",
// A 'div' for dynamically displaying the project's end date
"txtContent": "=toLocaleDateString([$ProjectEnd])",
// Converts the project end date from SharePoint list to a localized string format
"style": {
"position": "absolute",
// Uses absolute positioning for precise placement
"left": "=if([$ProjectEnd] == '', '0%', ((Date([$ProjectEnd]) - Date([$ProgrammeStart])) / (Date([$ProgrammeEnd]) - Date([$ProgrammeStart])) * 100 +2) + '%')",
// Calculates the position based on the end date's proportion of the total timeline, slightly adjusted to the right
"top": "2px",
// Positions the text slightly below the top line of its container
"color": "#555",
// Sets the text color to medium gray
"font-size": "10px"
// Maintains a small font size for clean appearance and readability
}
},
{
"elmType": "div",
// Another static 'div' to display an end date for a timeline
"txtContent": "31/12/2028",
// Static text representing the end date of the timeline
"style": {
"position": "absolute",
// Uses absolute positioning
"right": "0px",
// Aligns the text at the far right of its container
"top": "-20px",
// Positions the text 20 pixels above the parent container
"color": "#555",
// Text color set to a medium gray
"font-size": "10px"
// Uses a smaller font size for minimal visual impact
}
}
The final result looks really nice and helps display timelines and progress onto a Project Portal or similar with no load times!
Commentaires