SlideShare a Scribd company logo
React for Reuse
Creating reusable UI Components with React
MATTHEW JENSEN | TEAM LEAD | ATLASSIAN | @MATTHEWJENSEN
Introduction
About this Talk
Me
Confluence
Platform
Ecosystem
Me
Confluence
Platform
Ecosystem
Confluence
Platform
Me
Confluence
Platform
Ecosystem
Core APIs
Confluence Platform
Ecosystem
Front End Platform
Me
Confluence
Platform
Ecosystem
Ecosystem
Confluence Connect
Add-ons
Partnerships
Client Side
We use extended JavaScript
on the client side to create
the UI Components.
Server Side
In this example we use server
side JavaScript by using
NodeJs.
JavaScript Everywhere
Introduction
Add-on
Stack
Introduction
Add-on
Stack Express
(ACE)Atlassian Connect Express
Introduction
Add-on
Stack Express
(ACE)Atlassian Connect Express
Introduction
Add-on
Stack
ExpressACE
Introduction
Add-on
Stack
Introduction
Add-on
Stack
Static HTML
Introduction
Add-on
Stack
Sample Project
Confluence Issues Add-on
bitbucket.org/mjensen/confluence-issues-addon
bit.ly/2oqVGyb
Issues
Add-On
Overview
Issue List
Issue Macro
Issues
Add-On
Overview
Issue List
Issue Macro
Issues
Add-On
Overview
Issue List
Issue Macro
Issues
Add-On
Overview
Issue List
Issue Macro
Getting Started
Setting up the Project
bitbucket.org/atlassian/atlassian-connect-express
bit.ly/TTpDn1
Getting
Started
ACE
Project
Create the project
Use atlas-connect to create a project using the
confluence template.
confluence-issues-addon
public
routes
views
src
Components
A React Primer
Components
Functional
Components are Functions.
Classes
State
Components
function Clock(props) {

return <span>{moment().format()}</span>;

}
ReactDOM.render(

<Clock />,

document.getElementById('root')

);
Functional
Classes
State
Components
function Clock(props) {

return <span>{moment().format()}</span>;

}
ReactDOM.render(

<Clock />,

document.getElementById('root')

);
Functional
Classes
State
Components
function Clock(props) {

return <span>{moment().format()}</span>;

}
ReactDOM.render(

<Clock />,

document.getElementById('root')

);
Use a function to define a Component
Functional
Classes
State
f(x) Functional Components
Simple
Defining a component as a
function is very simple.
Stateless
Components defined as
functions have no local state.
Components are Classes.
Components
Functional
Classes
State
class Clock extends React.Component {

render() {

return <span>{moment().format()}</span>;

}

}
ReactDOM.render(

<Clock />,

document.getElementById('root')

);
Components
Functional
Classes
State
class Clock extends React.Component {

render() {

return <span>{moment().format()}</span>;

}

}
ReactDOM.render(

<Clock />,

document.getElementById('root')

);
Components
Functional
Classes
State
class Clock extends React.Component {

render() {

return <span>{moment().format()}</span>;

}

}
ReactDOM.render(

<Clock />,

document.getElementById('root')

);
Components
Functional
Classes
State
Use a class to define a Component
Components as Classes
Classes
Classes can benefit from
inheritance, composition and
other object orientation
strategies.
Stateful
Components defined as classes
can define their own state and
lifecycle.
Components have State.
Components
Functional
Classes
State
Components
class Clock extends React.Component {

constructor(props) {

super(props);

this.state = { date: moment() };

}


render() {

return (
<span>
{this.state.date.format()}
</span>
);

}

}
Functional
Classes
State
Components
class Clock extends React.Component {

constructor(props) {

super(props);

this.state = { date: moment() };

}


render() {

return (
<span>
{this.state.date.format()}
</span>
);

}

}
Functional
Classes
State
Components
Functional
Classes
class Clock extends React.Component {

constructor(props) {

super(props);

this.state = { date: moment() };

}


render() {

return (
<span>
{this.state.date.format()}
</span>
);

}

}
State
Class components can have state.
State Management
Component State
React provides simple state
management for each
component.
Centralised State
Redux is a centralised state
management system, often
simplifying your components
dramatically.
Components have a Lifecycle.
Components
Lifecycle
Props
Components
class Clock extends React.Component {
// ...
componentDidMount() {

// called after the component is added to the DOM

this.timerID = setInterval(() => this.tick(), 1000);

}



componentWillUnmount() {

// called before the component is removed from the DOM

clearInterval(this.timerID);

}



tick() {

this.setState({ date: moment() });
} 

// ...

}
Lifecycle
Props
Components
class Clock extends React.Component {
// ...
componentDidMount() {

// called after the component is added to the DOM

this.timerID = setInterval(() => this.tick(), 1000);

}



componentWillUnmount() {

// called before the component is removed from the DOM

clearInterval(this.timerID);

}



tick() {

this.setState({ date: moment() });
} 

// ...

}
Lifecycle
Props
Components
class Clock extends React.Component {
// ...
componentDidMount() {

// called after the component is added to the DOM

this.timerID = setInterval(() => this.tick(), 1000);

}



componentWillUnmount() {

// called before the component is removed from the DOM

clearInterval(this.timerID);

}



tick() {

this.setState({ date: moment() });
} 

// ...

}
Lifecycle
Props
Components
class Clock extends React.Component {
// ...
componentDidMount() {

// called after the component is added to the DOM

this.timerID = setInterval(() => this.tick(), 1000);

}



componentWillUnmount() {

// called before the component is removed from the DOM

clearInterval(this.timerID);

}



tick() {

this.setState({ date: moment() });
} 

// ...

}
Lifecycle
Props
The lifecycle methods update the state.
Props vs State.
Components
Lifecycle
Props
Components
// src/components/Clock.js
class Clock extends React.Component {
render() {

return (
<span>{this.state.date.format(this.props.dateFormat)}</span>
);

}

}
Clock.defaultProps = {

"dateFormat": 'MMMM Do YYYY, h:mm:ss'

};
// clock.js
ReactDOM.render(

<Clock />,

document.getElementById('clock')

);
ReactDOM.render(

<Clock dateFormat='h:mm:ss'/>,

document.getElementById('clock')

);
Lifecycle
Props
// src/components/Clock.js
class Clock extends React.Component {
render() {

return (
<span>{this.state.date.format(this.props.dateFormat)}</span>
);

}

}
Clock.defaultProps = {

"dateFormat": 'MMMM Do YYYY, h:mm:ss'

};
// clock.js
ReactDOM.render(

<Clock />,

document.getElementById('clock')

);
ReactDOM.render(

<Clock dateFormat='h:mm:ss'/>,

document.getElementById('clock')

);
Components
Lifecycle
Props
// src/components/Clock.js
class Clock extends React.Component {
render() {

return (
<span>{this.state.date.format(this.props.dateFormat)}</span>
);

}

}
Clock.defaultProps = {

"dateFormat": 'MMMM Do YYYY, h:mm:ss'

};
// clock.js
ReactDOM.render(

<Clock />,

document.getElementById('clock')

);
ReactDOM.render(

<Clock dateFormat='h:mm:ss'/>,

document.getElementById('clock')

);
Components
Lifecycle
Props
// src/components/Clock.js
class Clock extends React.Component {
render() {

return (
<span>{this.state.date.format(this.props.dateFormat)}</span>
);

}

}
Clock.defaultProps = {

"dateFormat": 'MMMM Do YYYY, h:mm:ss'

};
// clock.js
ReactDOM.render(

<Clock />,

document.getElementById('clock')

);
ReactDOM.render(

<Clock dateFormat='h:mm:ss'/>,

document.getElementById('clock')

);
Components
Lifecycle
Props
// src/components/Clock.js
class Clock extends React.Component {
render() {

return (
<span>{this.state.date.format(this.props.dateFormat)}</span>
);

}

}
Clock.defaultProps = {

"dateFormat": 'MMMM Do YYYY, h:mm:ss'

};
// clock.js
ReactDOM.render(

<Clock />,

document.getElementById('clock')

);
ReactDOM.render(

<Clock dateFormat='h:mm:ss'/>,

document.getElementById('clock')

);
Components
Lifecycle
Props
Props are set when created and are immutable.
Component Types
Container
Fetches the application data
and composes the User
Interface out of Presentation
components.
Presentation
Takes the data from a Container
component and presents it to
the user.
confluence-issues-addon
public
routes
views
src
components
containers
Project Components
Components of Confluence Issues Add-on
IssuesAppContainer
Decides what to display in the page body.
IssuesList
Displays the column headings and
an IssueListItem for each row.
IssuesListItem
Represents a row in the list of issues.
confluence-issues-addon
src
components
containers
confluence-issues-addon
src
components
containers
IssueAppContainer.js
list
IssueList.js
IssueListLineItem.js
IssueAppContainer
loadIssues(spaceKey) {

return (callback) => {

AP.require('request', function (request) {

request({

url: '/rest/api/content',

data: { … },

success: (data) => {

let issues = JSON.parse(data)
.results
.map(Issues.contentToIssue);
callback(localIssues);

}

});

});

}

}
Load Issues
loadIssues
loadIssues(spaceKey) {

return (callback) => {

AP.require('request', function (request) {

request({

url: '/rest/api/content',

data: { … },

success: (data) => {

let issues = JSON.parse(data)
.results
.map(Issues.contentToIssue);
callback(localIssues);

}

});

});

}

}
Load Issues
loadIssues
loadIssues(spaceKey) {

return (callback) => {

AP.require('request', function (request) {

request({

url: '/rest/api/content',

data: { … },

success: (data) => {

let issues = JSON.parse(data)
.results
.map(Issues.contentToIssue);
callback(localIssues);

}

});

});

}

}
Load Issues
loadIssues
loadIssues(spaceKey) {

return (callback) => {

AP.require('request', function (request) {

request({

url: '/rest/api/content',

data: { … },

success: (data) => {

let issues = JSON.parse(data)
.results
.map(Issues.contentToIssue);
callback(localIssues);

}

});

});

}

}
Load Issues
loadIssues
loadIssues(spaceKey) {

return (callback) => {

AP.require('request', function (request) {

request({

url: '/rest/api/content',

data: { … },

success: (data) => {

let issues = JSON.parse(data)
.results
.map(Issues.contentToIssue);
callback(localIssues);

}

});

});

}

}
Load Issues
loadIssues
Reusable function to load issues.
export default class IssueAppContainer extends React.Component {


componentDidMount() {

this.props.loadIssues((issues) => {

this.setState({issues: issues, loading: false})

});

}

render() {

let createIssueClick = …;

if (this.state.loading) {

return (<Loading/>);

} else {

let emptyState = (this.state.issues.length === 0);

if (emptyState) {

return (<IssueAppEmpty
createIssueClick={createIssueClick}/>)

} else {

return (<IssueAppPopulated
createIssueClick={createIssueClick}

issues={this.state.issues}/>)

}

}

}

}
Issue App
Components
Issue App
Issue List
Issue Macro
export default class IssueAppContainer extends React.Component {


componentDidMount() {

this.props.loadIssues((issues) => {

this.setState({issues: issues, loading: false})

});

}

render() {

let createIssueClick = …;

if (this.state.loading) {

return (<Loading/>);

} else {

let emptyState = (this.state.issues.length === 0);

if (emptyState) {

return (<IssueAppEmpty
createIssueClick={createIssueClick}/>)

} else {

return (<IssueAppPopulated
createIssueClick={createIssueClick}

issues={this.state.issues}/>)

}

}

}

}
Issue App
Components
Issue App
Issue List
Issue Macro
export default class IssueAppContainer extends React.Component {


componentDidMount() {

this.props.loadIssues((issues) => {

this.setState({issues: issues, loading: false})

});

}

render() {

let createIssueClick = …;

if (this.state.loading) {

return (<Loading/>);

} else {

let emptyState = (this.state.issues.length === 0);

if (emptyState) {

return (<IssueAppEmpty
createIssueClick={createIssueClick}/>)

} else {

return (<IssueAppPopulated
createIssueClick={createIssueClick}

issues={this.state.issues}/>)

}

}

}

}
Issue App
Components
Issue App
Issue List
Issue Macro
export default class IssueAppContainer extends React.Component {


componentDidMount() {

this.props.loadIssues((issues) => {

this.setState({issues: issues, loading: false})

});

}

render() {

let createIssueClick = …;

if (this.state.loading) {

return (<Loading/>);

} else {

let emptyState = (this.state.issues.length === 0);

if (emptyState) {

return (<IssueAppEmpty
createIssueClick={createIssueClick}/>)

} else {

return (<IssueAppPopulated
createIssueClick={createIssueClick}

issues={this.state.issues}/>)

}

}

}

}
Issue App
Components
Issue App
Issue List
Issue Macro
Use the state to decide what to render.
IssueList
[

{

"title": "Build…",

"type": "task",

"status": "new",
…

},
…

]
Issue App
Components
Issue App
Issue List
Issue Macro


export default class IssueList extends React.Component {

render() {

let issues = this.props.issues;

let fields = …;



return (

<table className="list-of-issues">

<IssueListHeadings fields={fields}/>

<tbody>

{issues.map((issue) => {

return (

<IssueLineItem
key={issue.key}
fields={fields}
issue={issue}/>)

})}

</tbody>

</table>

);

}

}
Issue App
Components
Issue App
Issue List
Issue Macro


export default class IssueList extends React.Component {

render() {

let issues = this.props.issues;

let fields = …;



return (

<table className="list-of-issues">

<IssueListHeadings fields={fields}/>

<tbody>

{issues.map((issue) => {

return (

<IssueLineItem
key={issue.key}
fields={fields}
issue={issue}/>)

})}

</tbody>

</table>

);

}

}
Issue App
Components
Issue App
Issue List
Issue Macro


export default class IssueList extends React.Component {

render() {

let issues = this.props.issues;

let fields = …;



return (

<table className="list-of-issues">

<IssueListHeadings fields={fields}/>

<tbody>

{issues.map((issue) => {

return (

<IssueLineItem
key={issue.key}
fields={fields}
issue={issue}/>)

})}

</tbody>

</table>

);

}

}
Issue App
Components
Issue App
Issue List
Issue Macro


export default class IssueList extends React.Component {

render() {

let issues = this.props.issues;

let fields = …;



return (

<table className="list-of-issues">

<IssueListHeadings fields={fields}/>

<tbody>

{issues.map((issue) => {

return (

<IssueLineItem
key={issue.key}
fields={fields}
issue={issue}/>)

})}

</tbody>

</table>

);

}

}
Presentation components have no state.
Issue App
Components
Issue App
Issue List
Issue Macro
Macro
This macro shows a list of issues.
Issue App
Issue List
Issue Macro
Issue App
Components
IssueMacroContainer
Issue App
Components
Issue App
Issue List
Issue Macro
export default class IssueMacroContainer extends React.Component {



componentDidMount() {

this.loadIssues((issues) => {

this.setState({issues: issues, loading: false})

});

}



render() {

if (this.state.loading) {

return (<Loading/>);

} else {

let emptyState = (this.state.issues.length === 0);

if (emptyState) {

return (<IssueMacroEmpty/>)

} else {

return (<IssueMacroPopulated
issues={this.state.issues}/>)

}

}

}

}
Issue App
Components
Issue App
Issue List
Issue Macro
export default class IssueMacroContainer extends React.Component {



componentDidMount() {

this.loadIssues((issues) => {

this.setState({issues: issues, loading: false})

});

}



render() {

if (this.state.loading) {

return (<Loading/>);

} else {

let emptyState = (this.state.issues.length === 0);

if (emptyState) {

return (<IssueMacroEmpty/>)

} else {

return (<IssueMacroPopulated
issues={this.state.issues}/>)

}

}

}

}
Issue App
Components
Issue App
Issue List
Issue Macro
export default class IssueMacroContainer extends React.Component {



componentDidMount() {

this.loadIssues((issues) => {

this.setState({issues: issues, loading: false})

});

}



render() {

if (this.state.loading) {

return (<Loading/>);

} else {

let emptyState = (this.state.issues.length === 0);

if (emptyState) {

return (<IssueMacroEmpty/>)

} else {

return (<IssueMacroPopulated
issues={this.state.issues}/>)

}

}

}

}
Issue App
Components
Issue App
Issue List
Issue Macro
export default class IssueMacroContainer extends React.Component {



componentDidMount() {

this.loadIssues((issues) => {

this.setState({issues: issues, loading: false})

});

}



render() {

if (this.state.loading) {

return (<Loading/>);

} else {

let emptyState = (this.state.issues.length === 0);

if (emptyState) {

return (<IssueMacroEmpty/>)

} else {

return (<IssueMacroPopulated
issues={this.state.issues}/>)

}

}

}

}
Same concept as IssueAppContainer.
Entry Points
Mapping Extensions to Components
MacroFull Page
Dialog
App.js
Macro.js
NewIssueDialog.js
confluence-issues-addon
src
components
containers
entry-points
Entry Point
Import
Context
Render
import IssueMacroContainer from '../containers/IssueMacroContainer';



let spaceKey = queryString.parse(location.search)["spaceKey"];



ReactDOM.render(<IssueMacroContainer

loadIssues={Issues.loadIssues(spaceKey)}/>,
document.getElementById("list-issues"));
Entry Point
Import
Context
Render
import IssueMacroContainer from '../containers/IssueMacroContainer';



let spaceKey = queryString.parse(location.search)["spaceKey"];



ReactDOM.render(<IssueMacroContainer

loadIssues={Issues.loadIssues(spaceKey)}/>,
document.getElementById("list-issues"));
import IssueMacroContainer from '../containers/IssueMacroContainer';



let spaceKey = queryString.parse(location.search)["spaceKey"];



ReactDOM.render(<IssueMacroContainer

loadIssues={Issues.loadIssues(spaceKey)}/>,
document.getElementById("list-issues"));
Entry Point
Import
Context
Render
import IssueMacroContainer from '../containers/IssueMacroContainer';



let spaceKey = queryString.parse(location.search)["spaceKey"];



ReactDOM.render(<IssueMacroContainer

loadIssues={Issues.loadIssues(spaceKey)}/>,
document.getElementById("list-issues"));
Entry Point
Import
Context
Render
An entry point will render the top component.
webpack takes modules with dependencies and
generates static assets representing those modules.
http://webpack.github.io/
confluence-issues-addon
webpack.config.js
Webpack
Configuration
Config
Entry
Output
module.exports = {

resolve: {root: [__dirname + path.sep + 'assets']},

devtool: 'source-map',

entry:{

app: './src/entry-points/App.js',

dialog: './src/entry-points/NewIssueDialog.js',

macro: './src/entry-points/Macro.js',

},

output: {

path: "./public/js",

filename: '[name].js'

},

plugins: [ … ],

module: {

loaders: [ … ]

}

};
module.exports = {

resolve: {root: [__dirname + path.sep + 'assets']},

devtool: 'source-map',

entry:{

app: './src/entry-points/App.js',

dialog: './src/entry-points/NewIssueDialog.js',

macro: './src/entry-points/Macro.js',

},

output: {

path: "./public/js",

filename: '[name].js'

},

plugins: [ … ],

module: {

loaders: [ … ]

}

};
Webpack
Configuration
Config
Entry
Output
module.exports = {

resolve: {root: [__dirname + path.sep + 'assets']},

devtool: 'source-map',

entry:{

app: './src/entry-points/App.js',

dialog: './src/entry-points/NewIssueDialog.js',

macro: './src/entry-points/Macro.js',

},

output: {

path: "./public/js",

filename: '[name].js'

},

plugins: [ … ],

module: {

loaders: [ … ]

}

};
Webpack
Configuration
Config
Entry
Output
module.exports = {

resolve: {root: [__dirname + path.sep + 'assets']},

devtool: 'source-map',

entry:{

app: './src/entry-points/App.js',

dialog: './src/entry-points/NewIssueDialog.js',

macro: './src/entry-points/Macro.js',

},

output: {

path: "./public/js",

filename: '[name].js'

},

plugins: [ … ],

module: {

loaders: [ … ]

}

};
Webpack
Configuration
Config
Entry
Output
Webpack will bundle your components into static
files that a browser will understand.
confluence-issues-addon
public
js
macro.js
{{!< layout}}

<div id="list-issues"/>

<script src="/js/macro.js"></script>
Recap
Component
State
Entry
HTML
Building
Using NPM to build your App
Building
Package
Bundle
Start
{

"name": "confluence-issues-addon",

"version": "0.0.1",

"private": true,

"scripts": {

"start": "node app.js",

"bundle": "webpack",

"watch": "webpack --watch",

"prestart": "webpack"

},

"dependencies": { … },

"devDependencies": {

"babel-core": "^6.9.0",

"babel-loader": "^6.2.4",

"babel-preset-es2015": "^6.9.0",

"babel-preset-react": "^6.5.0",

"webpack": "^1.13.0"

}

}
{

"name": "confluence-issues-addon",

"version": "0.0.1",

"private": true,

"scripts": {

"start": "node app.js",

"bundle": "webpack",

"watch": "webpack --watch",

"prestart": "webpack"

},

"dependencies": { … },

"devDependencies": {

"babel-core": "^6.9.0",

"babel-loader": "^6.2.4",

"babel-preset-es2015": "^6.9.0",

"babel-preset-react": "^6.5.0",

"webpack": "^1.13.0"

}

}
Building
Package
Bundle
Start
{

"name": "confluence-issues-addon",

"version": "0.0.1",

"private": true,

"scripts": {

"start": "node app.js",

"bundle": "webpack",

"watch": "webpack --watch",

"prestart": "webpack"

},

"dependencies": { … },

"devDependencies": {

"babel-core": "^6.9.0",

"babel-loader": "^6.2.4",

"babel-preset-es2015": "^6.9.0",

"babel-preset-react": "^6.5.0",

"webpack": "^1.13.0"

}

}
Building
Package
Bundle
Start
{

"name": "confluence-issues-addon",

"version": "0.0.1",

"private": true,

"scripts": {

"start": "node app.js",

"bundle": "webpack",

"watch": "webpack --watch",

"prestart": "webpack"

},

"dependencies": { … },

"devDependencies": {

"babel-core": "^6.9.0",

"babel-loader": "^6.2.4",

"babel-preset-es2015": "^6.9.0",

"babel-preset-react": "^6.5.0",

"webpack": "^1.13.0"

}

}
Building
Package
Bundle
Start
Babel is used to transpile the extended JavaScript.
Transpiling
ES2015 Extensions, modules,
dependencies, etc
Standalone files, compatible
with browsers, etc
Transpiling
Run a the webpack script
Use npm run bundle to run the webpack script we
defined in the package.json file.
Building
Package
Bundle
Start
Building
Package
Bundle
Start
Run a the webpack script
Use npm run bundle to run the webpack script we
defined in the package.json file.
Create the static JavaScript files
These files are self contained and browser compatible
JavaScript resources.
Building
Package
Bundle
Start
Client app.js
Refers to the application
entry point component
Server app.js
Default name for the Node.js
server entry point.
Two Apps
Create the project
Use npm start to start the node app, automatically
transpiling the client components.
Building
Package
Bundle
Start
Building
Package
Bundle
Start
Create the project
Use npm start to start the node app, automatically
transpiling the client components.
Building
Package
Bundle
Start
Create the project
Use npm start to start the node app, automatically
transpiling the client components.
Building
Package
Bundle
Start
Create the project
Use npm start to start the node app, automatically
transpiling the client components.
Node JS
Uses NPM to package both server and client side
modules.
Manually Package
You can still use NPM to bundle your client side
components
Dev Loop
Restart your add-on service and call webpack on
each change.
Recap
Quick recap of what we have learnt so far
Recap
Component
State
Entry
class Clock extends React.Component {

constructor(props) {

super(props);

this.state = {date: moment()};

}



componentDidMount() {

this.timerID = setInterval(() => this.tick(), 1000);

}



componentWillUnmount() {

clearInterval(this.timerID);

}



tick() {

this.setState({date: moment()});

}



render() {

return (<span>{this.state.date.format(this.props.dateFormat)}</span>);

}

}



Clock.defaultProps = {

'dateFormat': 'MMMM Do YYYY, h:mm:ss'

};



export default Clock;
Building
class Clock extends React.Component {

constructor(props) {

super(props);

this.state = {date: moment()};

}



componentDidMount() {

this.timerID = setInterval(() => this.tick(), 1000);

}



componentWillUnmount() {

clearInterval(this.timerID);

}



tick() {

this.setState({date: moment()});

}



render() {

return (<span>{this.state.date.format(this.props.dateFormat)}</span>);

}

}



Clock.defaultProps = {

'dateFormat': 'MMMM Do YYYY, h:mm:ss'

};



export default Clock;
Recap
Component
State
Entry
Building
class Clock extends React.Component {

constructor(props) {

super(props);

this.state = {date: moment()};

}



componentDidMount() {

this.timerID = setInterval(() => this.tick(), 1000);

}



componentWillUnmount() {

clearInterval(this.timerID);

}



tick() {

this.setState({date: moment()});

}



render() {

return (<span>{this.state.date.format(this.props.dateFormat)}</span>);

}

}



Clock.defaultProps = {

'dateFormat': 'MMMM Do YYYY, h:mm:ss'

};



export default Clock;
Recap
Component
State
Entry
Building
class Clock extends React.Component {

constructor(props) {

super(props);

this.state = {date: moment()};

}



componentDidMount() {

this.timerID = setInterval(() => this.tick(), 1000);

}



componentWillUnmount() {

clearInterval(this.timerID);

}



tick() {

this.setState({date: moment()});

}



render() {

return (<span>{this.state.date.format(this.props.dateFormat)}</span>);

}

}



Clock.defaultProps = {

'dateFormat': 'MMMM Do YYYY, h:mm:ss'

};



export default Clock;
Recap
Component
State
Entry
Building
ReactDOM.render(

<Clock dateFormat='MMMM Do YYYY, h:mm:ss'/>,

document.getElementById('clock')

);
Recap
Component
State
Entry
Building
module.exports = {

resolve: { root: [__dirname + path.sep + 'assets'] },

devtool: 'source-map',

entry:{

router: './assets/js/Clock'

},

output: {

path: "./public/js",

filename: '[name].js'

},

plugins: [ … ],

module: {

loaders: [ …]

}

};
Recap
Component
State
Entry
Building
module.exports = {

resolve: { root: [__dirname + path.sep + 'assets'] },

devtool: 'source-map',

entry:{

router: './assets/js/Clock'

},

output: {

path: "./public/js",

filename: '[name].js'

},

plugins: [ … ],

module: {

loaders: [ …]

}

};
Recap
Component
State
Entry
Building
{{!< layout}}

<div id="clock"/>

<script src="/js/clock.js"></script>
Recap
Component
State
Entry
Building
Recap
Component
State
Entry
Building
NPM
Use npm to manage webpack dependencies and
development lifecycle.
Onward
Related Concepts and Advanced Topics
Onward
AtlasKit
Other
Extensions React Components
AtlasKit components are
React Components.
NPM Modules
Define a dependency on the
AtlasKit component and
import it in to your own!
AtlasKit
Onward
AtlasKit
Other
Extensions
Onward
AtlasKit
Other
Extensions
Onward
AtlasKit
Other
Extensions
Onward
AtlasKit
Other
Extensions
Thank you!
MATTHEW JENSEN | TEAM LEAD | ATLASSIAN | @MATTHEWJENSEN

More Related Content

What's hot

AWS로 게임의 공통 기능 개발하기! - 채민관, 김민석, 한준식 :: AWS Game Master 온라인 세미나 #2
AWS로 게임의 공통 기능 개발하기! - 채민관, 김민석, 한준식 :: AWS Game Master 온라인 세미나 #2AWS로 게임의 공통 기능 개발하기! - 채민관, 김민석, 한준식 :: AWS Game Master 온라인 세미나 #2
AWS로 게임의 공통 기능 개발하기! - 채민관, 김민석, 한준식 :: AWS Game Master 온라인 세미나 #2
Amazon Web Services Korea
 
Architect proper segmentation for PCI DSS workloads on AWS - GRC306 - AWS re:...
Architect proper segmentation for PCI DSS workloads on AWS - GRC306 - AWS re:...Architect proper segmentation for PCI DSS workloads on AWS - GRC306 - AWS re:...
Architect proper segmentation for PCI DSS workloads on AWS - GRC306 - AWS re:...
Amazon Web Services
 
History of websites
History of websitesHistory of websites
History of websitesleahwilshh2
 
Introduction to Microservices
Introduction to MicroservicesIntroduction to Microservices
Introduction to Microservices
MahmoudZidan41
 
Getting Started with FIDO2
Getting Started with FIDO2Getting Started with FIDO2
Getting Started with FIDO2
FIDO Alliance
 
Amazon Cognito Deep Dive
Amazon Cognito Deep DiveAmazon Cognito Deep Dive
Amazon Cognito Deep Dive
Amazon Web Services
 
PrivateLink for Partners: Connectivity, Scale, Security (GPSTEC306) - AWS re:...
PrivateLink for Partners: Connectivity, Scale, Security (GPSTEC306) - AWS re:...PrivateLink for Partners: Connectivity, Scale, Security (GPSTEC306) - AWS re:...
PrivateLink for Partners: Connectivity, Scale, Security (GPSTEC306) - AWS re:...
Amazon Web Services
 
Microservices
MicroservicesMicroservices
Microservices
SmartBear
 
IAM Introduction and Best Practices
IAM Introduction and Best PracticesIAM Introduction and Best Practices
IAM Introduction and Best Practices
Amazon Web Services
 
IoT Apps with AWS IoT and Websockets
IoT Apps with AWS IoT and Websockets IoT Apps with AWS IoT and Websockets
IoT Apps with AWS IoT and Websockets
Amazon Web Services
 
Apache Camel Introduction & What's in the box
Apache Camel Introduction & What's in the boxApache Camel Introduction & What's in the box
Apache Camel Introduction & What's in the box
Claus Ibsen
 
Web automation using selenium.ppt
Web automation using selenium.pptWeb automation using selenium.ppt
Web automation using selenium.ppt
Ana Sarbescu
 
AWS 9월 웨비나 | AWS 데이터베이스 마이그레이션 서비스 활용하기
AWS 9월 웨비나 | AWS 데이터베이스 마이그레이션 서비스 활용하기AWS 9월 웨비나 | AWS 데이터베이스 마이그레이션 서비스 활용하기
AWS 9월 웨비나 | AWS 데이터베이스 마이그레이션 서비스 활용하기
Amazon Web Services Korea
 
Installation instruction of Testlink
Installation instruction of TestlinkInstallation instruction of Testlink
Installation instruction of Testlink
usha kannappan
 
Microservices
MicroservicesMicroservices
Microservices
Stephan Lindauer
 
WCAG
WCAGWCAG
Introduction to Microservices
Introduction to MicroservicesIntroduction to Microservices
Introduction to Microservices
Amazon Web Services
 

What's hot (17)

AWS로 게임의 공통 기능 개발하기! - 채민관, 김민석, 한준식 :: AWS Game Master 온라인 세미나 #2
AWS로 게임의 공통 기능 개발하기! - 채민관, 김민석, 한준식 :: AWS Game Master 온라인 세미나 #2AWS로 게임의 공통 기능 개발하기! - 채민관, 김민석, 한준식 :: AWS Game Master 온라인 세미나 #2
AWS로 게임의 공통 기능 개발하기! - 채민관, 김민석, 한준식 :: AWS Game Master 온라인 세미나 #2
 
Architect proper segmentation for PCI DSS workloads on AWS - GRC306 - AWS re:...
Architect proper segmentation for PCI DSS workloads on AWS - GRC306 - AWS re:...Architect proper segmentation for PCI DSS workloads on AWS - GRC306 - AWS re:...
Architect proper segmentation for PCI DSS workloads on AWS - GRC306 - AWS re:...
 
History of websites
History of websitesHistory of websites
History of websites
 
Introduction to Microservices
Introduction to MicroservicesIntroduction to Microservices
Introduction to Microservices
 
Getting Started with FIDO2
Getting Started with FIDO2Getting Started with FIDO2
Getting Started with FIDO2
 
Amazon Cognito Deep Dive
Amazon Cognito Deep DiveAmazon Cognito Deep Dive
Amazon Cognito Deep Dive
 
PrivateLink for Partners: Connectivity, Scale, Security (GPSTEC306) - AWS re:...
PrivateLink for Partners: Connectivity, Scale, Security (GPSTEC306) - AWS re:...PrivateLink for Partners: Connectivity, Scale, Security (GPSTEC306) - AWS re:...
PrivateLink for Partners: Connectivity, Scale, Security (GPSTEC306) - AWS re:...
 
Microservices
MicroservicesMicroservices
Microservices
 
IAM Introduction and Best Practices
IAM Introduction and Best PracticesIAM Introduction and Best Practices
IAM Introduction and Best Practices
 
IoT Apps with AWS IoT and Websockets
IoT Apps with AWS IoT and Websockets IoT Apps with AWS IoT and Websockets
IoT Apps with AWS IoT and Websockets
 
Apache Camel Introduction & What's in the box
Apache Camel Introduction & What's in the boxApache Camel Introduction & What's in the box
Apache Camel Introduction & What's in the box
 
Web automation using selenium.ppt
Web automation using selenium.pptWeb automation using selenium.ppt
Web automation using selenium.ppt
 
AWS 9월 웨비나 | AWS 데이터베이스 마이그레이션 서비스 활용하기
AWS 9월 웨비나 | AWS 데이터베이스 마이그레이션 서비스 활용하기AWS 9월 웨비나 | AWS 데이터베이스 마이그레이션 서비스 활용하기
AWS 9월 웨비나 | AWS 데이터베이스 마이그레이션 서비스 활용하기
 
Installation instruction of Testlink
Installation instruction of TestlinkInstallation instruction of Testlink
Installation instruction of Testlink
 
Microservices
MicroservicesMicroservices
Microservices
 
WCAG
WCAGWCAG
WCAG
 
Introduction to Microservices
Introduction to MicroservicesIntroduction to Microservices
Introduction to Microservices
 

Viewers also liked

Securing Your Atlassian Connect Add-on With JWT
Securing Your Atlassian Connect Add-on With JWTSecuring Your Atlassian Connect Add-on With JWT
Securing Your Atlassian Connect Add-on With JWT
Atlassian
 
Shipping to Server and Cloud with Docker
Shipping to Server and Cloud with DockerShipping to Server and Cloud with Docker
Shipping to Server and Cloud with Docker
Atlassian
 
How Bitbucket Pipelines Loads Connect UI Assets Super-fast
How Bitbucket Pipelines Loads Connect UI Assets Super-fastHow Bitbucket Pipelines Loads Connect UI Assets Super-fast
How Bitbucket Pipelines Loads Connect UI Assets Super-fast
Atlassian
 
Ten Battle-Tested Tips for Atlassian Connect Add-ons
Ten Battle-Tested Tips for Atlassian Connect Add-onsTen Battle-Tested Tips for Atlassian Connect Add-ons
Ten Battle-Tested Tips for Atlassian Connect Add-ons
Atlassian
 
Build a JIRA Server Add-on as a Microservice - You Can Do It!
Build a JIRA Server Add-on as a Microservice - You Can Do It!Build a JIRA Server Add-on as a Microservice - You Can Do It!
Build a JIRA Server Add-on as a Microservice - You Can Do It!
Atlassian
 
Tempo’s Journey Into the Cloud
Tempo’s Journey Into the CloudTempo’s Journey Into the Cloud
Tempo’s Journey Into the Cloud
Atlassian
 
Bringing Server Add-ons to the Cloud and Back Again
Bringing Server Add-ons to the Cloud and Back AgainBringing Server Add-ons to the Cloud and Back Again
Bringing Server Add-ons to the Cloud and Back Again
Atlassian
 
Building Search for Bitbucket Cloud
Building Search for Bitbucket CloudBuilding Search for Bitbucket Cloud
Building Search for Bitbucket Cloud
Atlassian
 
Atlassian Connect on Serverless Platforms: Low Cost Add-Ons
Atlassian Connect on Serverless Platforms: Low Cost Add-OnsAtlassian Connect on Serverless Platforms: Low Cost Add-Ons
Atlassian Connect on Serverless Platforms: Low Cost Add-Ons
Atlassian
 
Designing Add-ons for Atlassian Products, the Do’s & Don’ts
Designing Add-ons for Atlassian Products, the Do’s & Don’tsDesigning Add-ons for Atlassian Products, the Do’s & Don’ts
Designing Add-ons for Atlassian Products, the Do’s & Don’ts
Atlassian
 
5 Essential Techniques for Building Fault-tolerant Systems
5 Essential Techniques for Building Fault-tolerant Systems5 Essential Techniques for Building Fault-tolerant Systems
5 Essential Techniques for Building Fault-tolerant Systems
Atlassian
 
Know Thy Product: Tips from a Tester
Know Thy Product: Tips from a TesterKnow Thy Product: Tips from a Tester
Know Thy Product: Tips from a Tester
Atlassian
 
How to Make Customer Support Your Product's Greatest Feature
How to Make Customer Support Your Product's Greatest FeatureHow to Make Customer Support Your Product's Greatest Feature
How to Make Customer Support Your Product's Greatest Feature
Atlassian
 
How to Write a Chatbot that Gets Smarter
How to Write a Chatbot that Gets SmarterHow to Write a Chatbot that Gets Smarter
How to Write a Chatbot that Gets Smarter
Atlassian
 
What's New with Confluence Connect
What's New with Confluence ConnectWhat's New with Confluence Connect
What's New with Confluence Connect
Atlassian
 
12 Ways to Supercharge Your Connect Add-on
12 Ways to Supercharge Your Connect Add-on12 Ways to Supercharge Your Connect Add-on
12 Ways to Supercharge Your Connect Add-on
Atlassian
 
Launch into New Markets with JIRA Service Desk
Launch into New Markets with JIRA Service DeskLaunch into New Markets with JIRA Service Desk
Launch into New Markets with JIRA Service Desk
Atlassian
 
Server Add-ons for Front-end Developers
Server Add-ons for Front-end DevelopersServer Add-ons for Front-end Developers
Server Add-ons for Front-end Developers
Atlassian
 
Connect First, Ask Confluence Questions Later
Connect First, Ask Confluence Questions LaterConnect First, Ask Confluence Questions Later
Connect First, Ask Confluence Questions Later
Atlassian
 
Building for the Future: Design Your Add-on with Tomorrow in Mind
Building for the Future: Design Your Add-on with Tomorrow in MindBuilding for the Future: Design Your Add-on with Tomorrow in Mind
Building for the Future: Design Your Add-on with Tomorrow in Mind
Atlassian
 

Viewers also liked (20)

Securing Your Atlassian Connect Add-on With JWT
Securing Your Atlassian Connect Add-on With JWTSecuring Your Atlassian Connect Add-on With JWT
Securing Your Atlassian Connect Add-on With JWT
 
Shipping to Server and Cloud with Docker
Shipping to Server and Cloud with DockerShipping to Server and Cloud with Docker
Shipping to Server and Cloud with Docker
 
How Bitbucket Pipelines Loads Connect UI Assets Super-fast
How Bitbucket Pipelines Loads Connect UI Assets Super-fastHow Bitbucket Pipelines Loads Connect UI Assets Super-fast
How Bitbucket Pipelines Loads Connect UI Assets Super-fast
 
Ten Battle-Tested Tips for Atlassian Connect Add-ons
Ten Battle-Tested Tips for Atlassian Connect Add-onsTen Battle-Tested Tips for Atlassian Connect Add-ons
Ten Battle-Tested Tips for Atlassian Connect Add-ons
 
Build a JIRA Server Add-on as a Microservice - You Can Do It!
Build a JIRA Server Add-on as a Microservice - You Can Do It!Build a JIRA Server Add-on as a Microservice - You Can Do It!
Build a JIRA Server Add-on as a Microservice - You Can Do It!
 
Tempo’s Journey Into the Cloud
Tempo’s Journey Into the CloudTempo’s Journey Into the Cloud
Tempo’s Journey Into the Cloud
 
Bringing Server Add-ons to the Cloud and Back Again
Bringing Server Add-ons to the Cloud and Back AgainBringing Server Add-ons to the Cloud and Back Again
Bringing Server Add-ons to the Cloud and Back Again
 
Building Search for Bitbucket Cloud
Building Search for Bitbucket CloudBuilding Search for Bitbucket Cloud
Building Search for Bitbucket Cloud
 
Atlassian Connect on Serverless Platforms: Low Cost Add-Ons
Atlassian Connect on Serverless Platforms: Low Cost Add-OnsAtlassian Connect on Serverless Platforms: Low Cost Add-Ons
Atlassian Connect on Serverless Platforms: Low Cost Add-Ons
 
Designing Add-ons for Atlassian Products, the Do’s & Don’ts
Designing Add-ons for Atlassian Products, the Do’s & Don’tsDesigning Add-ons for Atlassian Products, the Do’s & Don’ts
Designing Add-ons for Atlassian Products, the Do’s & Don’ts
 
5 Essential Techniques for Building Fault-tolerant Systems
5 Essential Techniques for Building Fault-tolerant Systems5 Essential Techniques for Building Fault-tolerant Systems
5 Essential Techniques for Building Fault-tolerant Systems
 
Know Thy Product: Tips from a Tester
Know Thy Product: Tips from a TesterKnow Thy Product: Tips from a Tester
Know Thy Product: Tips from a Tester
 
How to Make Customer Support Your Product's Greatest Feature
How to Make Customer Support Your Product's Greatest FeatureHow to Make Customer Support Your Product's Greatest Feature
How to Make Customer Support Your Product's Greatest Feature
 
How to Write a Chatbot that Gets Smarter
How to Write a Chatbot that Gets SmarterHow to Write a Chatbot that Gets Smarter
How to Write a Chatbot that Gets Smarter
 
What's New with Confluence Connect
What's New with Confluence ConnectWhat's New with Confluence Connect
What's New with Confluence Connect
 
12 Ways to Supercharge Your Connect Add-on
12 Ways to Supercharge Your Connect Add-on12 Ways to Supercharge Your Connect Add-on
12 Ways to Supercharge Your Connect Add-on
 
Launch into New Markets with JIRA Service Desk
Launch into New Markets with JIRA Service DeskLaunch into New Markets with JIRA Service Desk
Launch into New Markets with JIRA Service Desk
 
Server Add-ons for Front-end Developers
Server Add-ons for Front-end DevelopersServer Add-ons for Front-end Developers
Server Add-ons for Front-end Developers
 
Connect First, Ask Confluence Questions Later
Connect First, Ask Confluence Questions LaterConnect First, Ask Confluence Questions Later
Connect First, Ask Confluence Questions Later
 
Building for the Future: Design Your Add-on with Tomorrow in Mind
Building for the Future: Design Your Add-on with Tomorrow in MindBuilding for the Future: Design Your Add-on with Tomorrow in Mind
Building for the Future: Design Your Add-on with Tomorrow in Mind
 

Similar to React for Re-use: Creating UI Components with Confluence Connect

React lecture
React lectureReact lecture
React lecture
Christoffer Noring
 
Let's react - Meetup
Let's react - MeetupLet's react - Meetup
Let's react - Meetup
RAJNISH KATHAROTIYA
 
Server side rendering with React and Symfony
Server side rendering with React and SymfonyServer side rendering with React and Symfony
Server side rendering with React and Symfony
Ignacio Martín
 
A full introductory guide to React
A full introductory guide to ReactA full introductory guide to React
A full introductory guide to React
Jean Carlo Emer
 
React - Start learning today
React - Start learning today React - Start learning today
React - Start learning today
Nitin Tyagi
 
Introduction to React JS for beginners
Introduction to React JS for beginners Introduction to React JS for beginners
Introduction to React JS for beginners
Varun Raj
 
React & The Art of Managing Complexity
React &  The Art of Managing ComplexityReact &  The Art of Managing Complexity
React & The Art of Managing Complexity
Ryan Anklam
 
Introduction to React for jQuery Developers
Introduction to React for jQuery DevelopersIntroduction to React for jQuery Developers
Introduction to React for jQuery Developers
Ronald Huereca
 
JSLab. Алексей Волков. "React на практике"
JSLab. Алексей Волков. "React на практике"JSLab. Алексей Волков. "React на практике"
JSLab. Алексей Волков. "React на практике"
GeeksLab Odessa
 
Universal JS Applications with React
Universal JS Applications with ReactUniversal JS Applications with React
Universal JS Applications with React
Thanh Trần Trọng
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testing
Visual Engineering
 
React Hooks Best Practices in 2022.pptx
React Hooks Best Practices in 2022.pptxReact Hooks Best Practices in 2022.pptx
React Hooks Best Practices in 2022.pptx
BOSC Tech Labs
 
Enhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order componentEnhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order component
Yao Nien Chung
 
React js
React jsReact js
React js
Rajesh Kolla
 
OttawaJS - React
OttawaJS - ReactOttawaJS - React
OttawaJS - React
rbl002
 
React & Redux for noobs
React & Redux for noobsReact & Redux for noobs
React & Redux for noobs
[T]echdencias
 
Fundamental Concepts of React JS for Beginners.pdf
Fundamental Concepts of React JS for Beginners.pdfFundamental Concepts of React JS for Beginners.pdf
Fundamental Concepts of React JS for Beginners.pdf
StephieJohn
 
react-hooks.pdf
react-hooks.pdfreact-hooks.pdf
react-hooks.pdf
chengbo xu
 

Similar to React for Re-use: Creating UI Components with Confluence Connect (20)

React lecture
React lectureReact lecture
React lecture
 
Let's react - Meetup
Let's react - MeetupLet's react - Meetup
Let's react - Meetup
 
Server side rendering with React and Symfony
Server side rendering with React and SymfonyServer side rendering with React and Symfony
Server side rendering with React and Symfony
 
A full introductory guide to React
A full introductory guide to ReactA full introductory guide to React
A full introductory guide to React
 
React - Start learning today
React - Start learning today React - Start learning today
React - Start learning today
 
Introduction to React JS for beginners
Introduction to React JS for beginners Introduction to React JS for beginners
Introduction to React JS for beginners
 
React & The Art of Managing Complexity
React &  The Art of Managing ComplexityReact &  The Art of Managing Complexity
React & The Art of Managing Complexity
 
Introduction to React for jQuery Developers
Introduction to React for jQuery DevelopersIntroduction to React for jQuery Developers
Introduction to React for jQuery Developers
 
JSLab. Алексей Волков. "React на практике"
JSLab. Алексей Волков. "React на практике"JSLab. Алексей Волков. "React на практике"
JSLab. Алексей Волков. "React на практике"
 
Universal JS Applications with React
Universal JS Applications with ReactUniversal JS Applications with React
Universal JS Applications with React
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testing
 
Intro react js
Intro react jsIntro react js
Intro react js
 
React Hooks Best Practices in 2022.pptx
React Hooks Best Practices in 2022.pptxReact Hooks Best Practices in 2022.pptx
React Hooks Best Practices in 2022.pptx
 
Lec7Handout.ppt
Lec7Handout.pptLec7Handout.ppt
Lec7Handout.ppt
 
Enhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order componentEnhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order component
 
React js
React jsReact js
React js
 
OttawaJS - React
OttawaJS - ReactOttawaJS - React
OttawaJS - React
 
React & Redux for noobs
React & Redux for noobsReact & Redux for noobs
React & Redux for noobs
 
Fundamental Concepts of React JS for Beginners.pdf
Fundamental Concepts of React JS for Beginners.pdfFundamental Concepts of React JS for Beginners.pdf
Fundamental Concepts of React JS for Beginners.pdf
 
react-hooks.pdf
react-hooks.pdfreact-hooks.pdf
react-hooks.pdf
 

More from Atlassian

International Women's Day 2020
International Women's Day 2020International Women's Day 2020
International Women's Day 2020
Atlassian
 
10 emerging trends that will unbreak your workplace in 2020
10 emerging trends that will unbreak your workplace in 202010 emerging trends that will unbreak your workplace in 2020
10 emerging trends that will unbreak your workplace in 2020
Atlassian
 
Forge App Showcase
Forge App ShowcaseForge App Showcase
Forge App Showcase
Atlassian
 
Let's Build an Editor Macro with Forge UI
Let's Build an Editor Macro with Forge UILet's Build an Editor Macro with Forge UI
Let's Build an Editor Macro with Forge UI
Atlassian
 
Meet the Forge Runtime
Meet the Forge RuntimeMeet the Forge Runtime
Meet the Forge Runtime
Atlassian
 
Forge UI: A New Way to Customize the Atlassian User Experience
Forge UI: A New Way to Customize the Atlassian User ExperienceForge UI: A New Way to Customize the Atlassian User Experience
Forge UI: A New Way to Customize the Atlassian User Experience
Atlassian
 
Take Action with Forge Triggers
Take Action with Forge TriggersTake Action with Forge Triggers
Take Action with Forge Triggers
Atlassian
 
Observability and Troubleshooting in Forge
Observability and Troubleshooting in ForgeObservability and Troubleshooting in Forge
Observability and Troubleshooting in Forge
Atlassian
 
Trusted by Default: The Forge Security & Privacy Model
Trusted by Default: The Forge Security & Privacy ModelTrusted by Default: The Forge Security & Privacy Model
Trusted by Default: The Forge Security & Privacy Model
Atlassian
 
Designing Forge UI: A Story of Designing an App UI System
Designing Forge UI: A Story of Designing an App UI SystemDesigning Forge UI: A Story of Designing an App UI System
Designing Forge UI: A Story of Designing an App UI System
Atlassian
 
Forge: Under the Hood
Forge: Under the HoodForge: Under the Hood
Forge: Under the Hood
Atlassian
 
Access to User Activities - Activity Platform APIs
Access to User Activities - Activity Platform APIsAccess to User Activities - Activity Platform APIs
Access to User Activities - Activity Platform APIs
Atlassian
 
Design Your Next App with the Atlassian Vendor Sketch Plugin
Design Your Next App with the Atlassian Vendor Sketch PluginDesign Your Next App with the Atlassian Vendor Sketch Plugin
Design Your Next App with the Atlassian Vendor Sketch Plugin
Atlassian
 
Tear Up Your Roadmap and Get Out of the Building
Tear Up Your Roadmap and Get Out of the BuildingTear Up Your Roadmap and Get Out of the Building
Tear Up Your Roadmap and Get Out of the Building
Atlassian
 
Nailing Measurement: a Framework for Measuring Metrics that Matter
Nailing Measurement: a Framework for Measuring Metrics that MatterNailing Measurement: a Framework for Measuring Metrics that Matter
Nailing Measurement: a Framework for Measuring Metrics that Matter
Atlassian
 
Building Apps With Color Blind Users in Mind
Building Apps With Color Blind Users in MindBuilding Apps With Color Blind Users in Mind
Building Apps With Color Blind Users in Mind
Atlassian
 
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...
Atlassian
 
Beyond Diversity: A Guide to Building Balanced Teams
Beyond Diversity: A Guide to Building Balanced TeamsBeyond Diversity: A Guide to Building Balanced Teams
Beyond Diversity: A Guide to Building Balanced Teams
Atlassian
 
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed Team
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed TeamThe Road(map) to Las Vegas - The Story of an Emerging Self-Managed Team
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed Team
Atlassian
 
Building Apps With Enterprise in Mind
Building Apps With Enterprise in MindBuilding Apps With Enterprise in Mind
Building Apps With Enterprise in Mind
Atlassian
 

More from Atlassian (20)

International Women's Day 2020
International Women's Day 2020International Women's Day 2020
International Women's Day 2020
 
10 emerging trends that will unbreak your workplace in 2020
10 emerging trends that will unbreak your workplace in 202010 emerging trends that will unbreak your workplace in 2020
10 emerging trends that will unbreak your workplace in 2020
 
Forge App Showcase
Forge App ShowcaseForge App Showcase
Forge App Showcase
 
Let's Build an Editor Macro with Forge UI
Let's Build an Editor Macro with Forge UILet's Build an Editor Macro with Forge UI
Let's Build an Editor Macro with Forge UI
 
Meet the Forge Runtime
Meet the Forge RuntimeMeet the Forge Runtime
Meet the Forge Runtime
 
Forge UI: A New Way to Customize the Atlassian User Experience
Forge UI: A New Way to Customize the Atlassian User ExperienceForge UI: A New Way to Customize the Atlassian User Experience
Forge UI: A New Way to Customize the Atlassian User Experience
 
Take Action with Forge Triggers
Take Action with Forge TriggersTake Action with Forge Triggers
Take Action with Forge Triggers
 
Observability and Troubleshooting in Forge
Observability and Troubleshooting in ForgeObservability and Troubleshooting in Forge
Observability and Troubleshooting in Forge
 
Trusted by Default: The Forge Security & Privacy Model
Trusted by Default: The Forge Security & Privacy ModelTrusted by Default: The Forge Security & Privacy Model
Trusted by Default: The Forge Security & Privacy Model
 
Designing Forge UI: A Story of Designing an App UI System
Designing Forge UI: A Story of Designing an App UI SystemDesigning Forge UI: A Story of Designing an App UI System
Designing Forge UI: A Story of Designing an App UI System
 
Forge: Under the Hood
Forge: Under the HoodForge: Under the Hood
Forge: Under the Hood
 
Access to User Activities - Activity Platform APIs
Access to User Activities - Activity Platform APIsAccess to User Activities - Activity Platform APIs
Access to User Activities - Activity Platform APIs
 
Design Your Next App with the Atlassian Vendor Sketch Plugin
Design Your Next App with the Atlassian Vendor Sketch PluginDesign Your Next App with the Atlassian Vendor Sketch Plugin
Design Your Next App with the Atlassian Vendor Sketch Plugin
 
Tear Up Your Roadmap and Get Out of the Building
Tear Up Your Roadmap and Get Out of the BuildingTear Up Your Roadmap and Get Out of the Building
Tear Up Your Roadmap and Get Out of the Building
 
Nailing Measurement: a Framework for Measuring Metrics that Matter
Nailing Measurement: a Framework for Measuring Metrics that MatterNailing Measurement: a Framework for Measuring Metrics that Matter
Nailing Measurement: a Framework for Measuring Metrics that Matter
 
Building Apps With Color Blind Users in Mind
Building Apps With Color Blind Users in MindBuilding Apps With Color Blind Users in Mind
Building Apps With Color Blind Users in Mind
 
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...
 
Beyond Diversity: A Guide to Building Balanced Teams
Beyond Diversity: A Guide to Building Balanced TeamsBeyond Diversity: A Guide to Building Balanced Teams
Beyond Diversity: A Guide to Building Balanced Teams
 
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed Team
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed TeamThe Road(map) to Las Vegas - The Story of an Emerging Self-Managed Team
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed Team
 
Building Apps With Enterprise in Mind
Building Apps With Enterprise in MindBuilding Apps With Enterprise in Mind
Building Apps With Enterprise in Mind
 

Recently uploaded

Lecture 1 Introduction to games development
Lecture 1 Introduction to games developmentLecture 1 Introduction to games development
Lecture 1 Introduction to games development
abdulrafaychaudhry
 
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Globus
 
A Comprehensive Look at Generative AI in Retail App Testing.pdf
A Comprehensive Look at Generative AI in Retail App Testing.pdfA Comprehensive Look at Generative AI in Retail App Testing.pdf
A Comprehensive Look at Generative AI in Retail App Testing.pdf
kalichargn70th171
 
RISE with SAP and Journey to the Intelligent Enterprise
RISE with SAP and Journey to the Intelligent EnterpriseRISE with SAP and Journey to the Intelligent Enterprise
RISE with SAP and Journey to the Intelligent Enterprise
Srikant77
 
AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing SuiteAI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
Google
 
May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
Adele Miller
 
Navigating the Metaverse: A Journey into Virtual Evolution"
Navigating the Metaverse: A Journey into Virtual Evolution"Navigating the Metaverse: A Journey into Virtual Evolution"
Navigating the Metaverse: A Journey into Virtual Evolution"
Donna Lenk
 
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
Tier1 app
 
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, BetterWebinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
XfilesPro
 
Enterprise Resource Planning System in Telangana
Enterprise Resource Planning System in TelanganaEnterprise Resource Planning System in Telangana
Enterprise Resource Planning System in Telangana
NYGGS Automation Suite
 
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
informapgpstrackings
 
Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604
Fermin Galan
 
Enhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdf
Enhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdfEnhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdf
Enhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdf
Jay Das
 
How Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptxHow Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptx
wottaspaceseo
 
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Globus
 
Globus Compute Introduction - GlobusWorld 2024
Globus Compute Introduction - GlobusWorld 2024Globus Compute Introduction - GlobusWorld 2024
Globus Compute Introduction - GlobusWorld 2024
Globus
 
BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024
Ortus Solutions, Corp
 
First Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User EndpointsFirst Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User Endpoints
Globus
 
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
Juraj Vysvader
 
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar
 

Recently uploaded (20)

Lecture 1 Introduction to games development
Lecture 1 Introduction to games developmentLecture 1 Introduction to games development
Lecture 1 Introduction to games development
 
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
 
A Comprehensive Look at Generative AI in Retail App Testing.pdf
A Comprehensive Look at Generative AI in Retail App Testing.pdfA Comprehensive Look at Generative AI in Retail App Testing.pdf
A Comprehensive Look at Generative AI in Retail App Testing.pdf
 
RISE with SAP and Journey to the Intelligent Enterprise
RISE with SAP and Journey to the Intelligent EnterpriseRISE with SAP and Journey to the Intelligent Enterprise
RISE with SAP and Journey to the Intelligent Enterprise
 
AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing SuiteAI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
 
May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
 
Navigating the Metaverse: A Journey into Virtual Evolution"
Navigating the Metaverse: A Journey into Virtual Evolution"Navigating the Metaverse: A Journey into Virtual Evolution"
Navigating the Metaverse: A Journey into Virtual Evolution"
 
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
 
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, BetterWebinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
 
Enterprise Resource Planning System in Telangana
Enterprise Resource Planning System in TelanganaEnterprise Resource Planning System in Telangana
Enterprise Resource Planning System in Telangana
 
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
 
Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604
 
Enhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdf
Enhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdfEnhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdf
Enhancing Project Management Efficiency_ Leveraging AI Tools like ChatGPT.pdf
 
How Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptxHow Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptx
 
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
 
Globus Compute Introduction - GlobusWorld 2024
Globus Compute Introduction - GlobusWorld 2024Globus Compute Introduction - GlobusWorld 2024
Globus Compute Introduction - GlobusWorld 2024
 
BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024
 
First Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User EndpointsFirst Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User Endpoints
 
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
 
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBroker
 

React for Re-use: Creating UI Components with Confluence Connect

  • 1. React for Reuse Creating reusable UI Components with React MATTHEW JENSEN | TEAM LEAD | ATLASSIAN | @MATTHEWJENSEN
  • 7. Client Side We use extended JavaScript on the client side to create the UI Components. Server Side In this example we use server side JavaScript by using NodeJs. JavaScript Everywhere
  • 23. Getting Started ACE Project Create the project Use atlas-connect to create a project using the confluence template.
  • 27. Components function Clock(props) {
 return <span>{moment().format()}</span>;
 } ReactDOM.render(
 <Clock />,
 document.getElementById('root')
 ); Functional Classes State
  • 28. Components function Clock(props) {
 return <span>{moment().format()}</span>;
 } ReactDOM.render(
 <Clock />,
 document.getElementById('root')
 ); Functional Classes State
  • 29. Components function Clock(props) {
 return <span>{moment().format()}</span>;
 } ReactDOM.render(
 <Clock />,
 document.getElementById('root')
 ); Use a function to define a Component Functional Classes State
  • 30. f(x) Functional Components Simple Defining a component as a function is very simple. Stateless Components defined as functions have no local state.
  • 32. class Clock extends React.Component {
 render() {
 return <span>{moment().format()}</span>;
 }
 } ReactDOM.render(
 <Clock />,
 document.getElementById('root')
 ); Components Functional Classes State
  • 33. class Clock extends React.Component {
 render() {
 return <span>{moment().format()}</span>;
 }
 } ReactDOM.render(
 <Clock />,
 document.getElementById('root')
 ); Components Functional Classes State
  • 34. class Clock extends React.Component {
 render() {
 return <span>{moment().format()}</span>;
 }
 } ReactDOM.render(
 <Clock />,
 document.getElementById('root')
 ); Components Functional Classes State Use a class to define a Component
  • 35. Components as Classes Classes Classes can benefit from inheritance, composition and other object orientation strategies. Stateful Components defined as classes can define their own state and lifecycle.
  • 37. Components class Clock extends React.Component {
 constructor(props) {
 super(props);
 this.state = { date: moment() };
 } 
 render() {
 return ( <span> {this.state.date.format()} </span> );
 }
 } Functional Classes State
  • 38. Components class Clock extends React.Component {
 constructor(props) {
 super(props);
 this.state = { date: moment() };
 } 
 render() {
 return ( <span> {this.state.date.format()} </span> );
 }
 } Functional Classes State
  • 39. Components Functional Classes class Clock extends React.Component {
 constructor(props) {
 super(props);
 this.state = { date: moment() };
 } 
 render() {
 return ( <span> {this.state.date.format()} </span> );
 }
 } State Class components can have state.
  • 40. State Management Component State React provides simple state management for each component. Centralised State Redux is a centralised state management system, often simplifying your components dramatically.
  • 41. Components have a Lifecycle. Components Lifecycle Props
  • 42. Components class Clock extends React.Component { // ... componentDidMount() {
 // called after the component is added to the DOM
 this.timerID = setInterval(() => this.tick(), 1000);
 }
 
 componentWillUnmount() {
 // called before the component is removed from the DOM
 clearInterval(this.timerID);
 }
 
 tick() {
 this.setState({ date: moment() }); } 
 // ...
 } Lifecycle Props
  • 43. Components class Clock extends React.Component { // ... componentDidMount() {
 // called after the component is added to the DOM
 this.timerID = setInterval(() => this.tick(), 1000);
 }
 
 componentWillUnmount() {
 // called before the component is removed from the DOM
 clearInterval(this.timerID);
 }
 
 tick() {
 this.setState({ date: moment() }); } 
 // ...
 } Lifecycle Props
  • 44. Components class Clock extends React.Component { // ... componentDidMount() {
 // called after the component is added to the DOM
 this.timerID = setInterval(() => this.tick(), 1000);
 }
 
 componentWillUnmount() {
 // called before the component is removed from the DOM
 clearInterval(this.timerID);
 }
 
 tick() {
 this.setState({ date: moment() }); } 
 // ...
 } Lifecycle Props
  • 45. Components class Clock extends React.Component { // ... componentDidMount() {
 // called after the component is added to the DOM
 this.timerID = setInterval(() => this.tick(), 1000);
 }
 
 componentWillUnmount() {
 // called before the component is removed from the DOM
 clearInterval(this.timerID);
 }
 
 tick() {
 this.setState({ date: moment() }); } 
 // ...
 } Lifecycle Props The lifecycle methods update the state.
  • 47. Components // src/components/Clock.js class Clock extends React.Component { render() {
 return ( <span>{this.state.date.format(this.props.dateFormat)}</span> );
 }
 } Clock.defaultProps = {
 "dateFormat": 'MMMM Do YYYY, h:mm:ss'
 }; // clock.js ReactDOM.render(
 <Clock />,
 document.getElementById('clock')
 ); ReactDOM.render(
 <Clock dateFormat='h:mm:ss'/>,
 document.getElementById('clock')
 ); Lifecycle Props
  • 48. // src/components/Clock.js class Clock extends React.Component { render() {
 return ( <span>{this.state.date.format(this.props.dateFormat)}</span> );
 }
 } Clock.defaultProps = {
 "dateFormat": 'MMMM Do YYYY, h:mm:ss'
 }; // clock.js ReactDOM.render(
 <Clock />,
 document.getElementById('clock')
 ); ReactDOM.render(
 <Clock dateFormat='h:mm:ss'/>,
 document.getElementById('clock')
 ); Components Lifecycle Props
  • 49. // src/components/Clock.js class Clock extends React.Component { render() {
 return ( <span>{this.state.date.format(this.props.dateFormat)}</span> );
 }
 } Clock.defaultProps = {
 "dateFormat": 'MMMM Do YYYY, h:mm:ss'
 }; // clock.js ReactDOM.render(
 <Clock />,
 document.getElementById('clock')
 ); ReactDOM.render(
 <Clock dateFormat='h:mm:ss'/>,
 document.getElementById('clock')
 ); Components Lifecycle Props
  • 50. // src/components/Clock.js class Clock extends React.Component { render() {
 return ( <span>{this.state.date.format(this.props.dateFormat)}</span> );
 }
 } Clock.defaultProps = {
 "dateFormat": 'MMMM Do YYYY, h:mm:ss'
 }; // clock.js ReactDOM.render(
 <Clock />,
 document.getElementById('clock')
 ); ReactDOM.render(
 <Clock dateFormat='h:mm:ss'/>,
 document.getElementById('clock')
 ); Components Lifecycle Props
  • 51. // src/components/Clock.js class Clock extends React.Component { render() {
 return ( <span>{this.state.date.format(this.props.dateFormat)}</span> );
 }
 } Clock.defaultProps = {
 "dateFormat": 'MMMM Do YYYY, h:mm:ss'
 }; // clock.js ReactDOM.render(
 <Clock />,
 document.getElementById('clock')
 ); ReactDOM.render(
 <Clock dateFormat='h:mm:ss'/>,
 document.getElementById('clock')
 ); Components Lifecycle Props Props are set when created and are immutable.
  • 52. Component Types Container Fetches the application data and composes the User Interface out of Presentation components. Presentation Takes the data from a Container component and presents it to the user.
  • 54. Project Components Components of Confluence Issues Add-on
  • 55.
  • 56. IssuesAppContainer Decides what to display in the page body.
  • 57. IssuesList Displays the column headings and an IssueListItem for each row.
  • 58. IssuesListItem Represents a row in the list of issues.
  • 62. loadIssues(spaceKey) {
 return (callback) => {
 AP.require('request', function (request) {
 request({
 url: '/rest/api/content',
 data: { … },
 success: (data) => {
 let issues = JSON.parse(data) .results .map(Issues.contentToIssue); callback(localIssues);
 }
 });
 });
 }
 } Load Issues loadIssues
  • 63. loadIssues(spaceKey) {
 return (callback) => {
 AP.require('request', function (request) {
 request({
 url: '/rest/api/content',
 data: { … },
 success: (data) => {
 let issues = JSON.parse(data) .results .map(Issues.contentToIssue); callback(localIssues);
 }
 });
 });
 }
 } Load Issues loadIssues
  • 64. loadIssues(spaceKey) {
 return (callback) => {
 AP.require('request', function (request) {
 request({
 url: '/rest/api/content',
 data: { … },
 success: (data) => {
 let issues = JSON.parse(data) .results .map(Issues.contentToIssue); callback(localIssues);
 }
 });
 });
 }
 } Load Issues loadIssues
  • 65. loadIssues(spaceKey) {
 return (callback) => {
 AP.require('request', function (request) {
 request({
 url: '/rest/api/content',
 data: { … },
 success: (data) => {
 let issues = JSON.parse(data) .results .map(Issues.contentToIssue); callback(localIssues);
 }
 });
 });
 }
 } Load Issues loadIssues
  • 66. loadIssues(spaceKey) {
 return (callback) => {
 AP.require('request', function (request) {
 request({
 url: '/rest/api/content',
 data: { … },
 success: (data) => {
 let issues = JSON.parse(data) .results .map(Issues.contentToIssue); callback(localIssues);
 }
 });
 });
 }
 } Load Issues loadIssues Reusable function to load issues.
  • 67. export default class IssueAppContainer extends React.Component { 
 componentDidMount() {
 this.props.loadIssues((issues) => {
 this.setState({issues: issues, loading: false})
 });
 }
 render() {
 let createIssueClick = …;
 if (this.state.loading) {
 return (<Loading/>);
 } else {
 let emptyState = (this.state.issues.length === 0);
 if (emptyState) {
 return (<IssueAppEmpty createIssueClick={createIssueClick}/>)
 } else {
 return (<IssueAppPopulated createIssueClick={createIssueClick}
 issues={this.state.issues}/>)
 }
 }
 }
 } Issue App Components Issue App Issue List Issue Macro
  • 68. export default class IssueAppContainer extends React.Component { 
 componentDidMount() {
 this.props.loadIssues((issues) => {
 this.setState({issues: issues, loading: false})
 });
 }
 render() {
 let createIssueClick = …;
 if (this.state.loading) {
 return (<Loading/>);
 } else {
 let emptyState = (this.state.issues.length === 0);
 if (emptyState) {
 return (<IssueAppEmpty createIssueClick={createIssueClick}/>)
 } else {
 return (<IssueAppPopulated createIssueClick={createIssueClick}
 issues={this.state.issues}/>)
 }
 }
 }
 } Issue App Components Issue App Issue List Issue Macro
  • 69. export default class IssueAppContainer extends React.Component { 
 componentDidMount() {
 this.props.loadIssues((issues) => {
 this.setState({issues: issues, loading: false})
 });
 }
 render() {
 let createIssueClick = …;
 if (this.state.loading) {
 return (<Loading/>);
 } else {
 let emptyState = (this.state.issues.length === 0);
 if (emptyState) {
 return (<IssueAppEmpty createIssueClick={createIssueClick}/>)
 } else {
 return (<IssueAppPopulated createIssueClick={createIssueClick}
 issues={this.state.issues}/>)
 }
 }
 }
 } Issue App Components Issue App Issue List Issue Macro
  • 70. export default class IssueAppContainer extends React.Component { 
 componentDidMount() {
 this.props.loadIssues((issues) => {
 this.setState({issues: issues, loading: false})
 });
 }
 render() {
 let createIssueClick = …;
 if (this.state.loading) {
 return (<Loading/>);
 } else {
 let emptyState = (this.state.issues.length === 0);
 if (emptyState) {
 return (<IssueAppEmpty createIssueClick={createIssueClick}/>)
 } else {
 return (<IssueAppPopulated createIssueClick={createIssueClick}
 issues={this.state.issues}/>)
 }
 }
 }
 } Issue App Components Issue App Issue List Issue Macro Use the state to decide what to render.
  • 72. Issue App Components Issue App Issue List Issue Macro 
 export default class IssueList extends React.Component {
 render() {
 let issues = this.props.issues;
 let fields = …;
 
 return (
 <table className="list-of-issues">
 <IssueListHeadings fields={fields}/>
 <tbody>
 {issues.map((issue) => {
 return (
 <IssueLineItem key={issue.key} fields={fields} issue={issue}/>)
 })}
 </tbody>
 </table>
 );
 }
 }
  • 73. Issue App Components Issue App Issue List Issue Macro 
 export default class IssueList extends React.Component {
 render() {
 let issues = this.props.issues;
 let fields = …;
 
 return (
 <table className="list-of-issues">
 <IssueListHeadings fields={fields}/>
 <tbody>
 {issues.map((issue) => {
 return (
 <IssueLineItem key={issue.key} fields={fields} issue={issue}/>)
 })}
 </tbody>
 </table>
 );
 }
 }
  • 74. Issue App Components Issue App Issue List Issue Macro 
 export default class IssueList extends React.Component {
 render() {
 let issues = this.props.issues;
 let fields = …;
 
 return (
 <table className="list-of-issues">
 <IssueListHeadings fields={fields}/>
 <tbody>
 {issues.map((issue) => {
 return (
 <IssueLineItem key={issue.key} fields={fields} issue={issue}/>)
 })}
 </tbody>
 </table>
 );
 }
 }
  • 75. Issue App Components Issue App Issue List Issue Macro 
 export default class IssueList extends React.Component {
 render() {
 let issues = this.props.issues;
 let fields = …;
 
 return (
 <table className="list-of-issues">
 <IssueListHeadings fields={fields}/>
 <tbody>
 {issues.map((issue) => {
 return (
 <IssueLineItem key={issue.key} fields={fields} issue={issue}/>)
 })}
 </tbody>
 </table>
 );
 }
 } Presentation components have no state.
  • 77. Macro This macro shows a list of issues. Issue App Issue List Issue Macro Issue App Components
  • 79. Issue App Components Issue App Issue List Issue Macro export default class IssueMacroContainer extends React.Component {
 
 componentDidMount() {
 this.loadIssues((issues) => {
 this.setState({issues: issues, loading: false})
 });
 }
 
 render() {
 if (this.state.loading) {
 return (<Loading/>);
 } else {
 let emptyState = (this.state.issues.length === 0);
 if (emptyState) {
 return (<IssueMacroEmpty/>)
 } else {
 return (<IssueMacroPopulated issues={this.state.issues}/>)
 }
 }
 }
 }
  • 80. Issue App Components Issue App Issue List Issue Macro export default class IssueMacroContainer extends React.Component {
 
 componentDidMount() {
 this.loadIssues((issues) => {
 this.setState({issues: issues, loading: false})
 });
 }
 
 render() {
 if (this.state.loading) {
 return (<Loading/>);
 } else {
 let emptyState = (this.state.issues.length === 0);
 if (emptyState) {
 return (<IssueMacroEmpty/>)
 } else {
 return (<IssueMacroPopulated issues={this.state.issues}/>)
 }
 }
 }
 }
  • 81. Issue App Components Issue App Issue List Issue Macro export default class IssueMacroContainer extends React.Component {
 
 componentDidMount() {
 this.loadIssues((issues) => {
 this.setState({issues: issues, loading: false})
 });
 }
 
 render() {
 if (this.state.loading) {
 return (<Loading/>);
 } else {
 let emptyState = (this.state.issues.length === 0);
 if (emptyState) {
 return (<IssueMacroEmpty/>)
 } else {
 return (<IssueMacroPopulated issues={this.state.issues}/>)
 }
 }
 }
 }
  • 82. Issue App Components Issue App Issue List Issue Macro export default class IssueMacroContainer extends React.Component {
 
 componentDidMount() {
 this.loadIssues((issues) => {
 this.setState({issues: issues, loading: false})
 });
 }
 
 render() {
 if (this.state.loading) {
 return (<Loading/>);
 } else {
 let emptyState = (this.state.issues.length === 0);
 if (emptyState) {
 return (<IssueMacroEmpty/>)
 } else {
 return (<IssueMacroPopulated issues={this.state.issues}/>)
 }
 }
 }
 } Same concept as IssueAppContainer.
  • 86. Entry Point Import Context Render import IssueMacroContainer from '../containers/IssueMacroContainer';
 
 let spaceKey = queryString.parse(location.search)["spaceKey"];
 
 ReactDOM.render(<IssueMacroContainer
 loadIssues={Issues.loadIssues(spaceKey)}/>, document.getElementById("list-issues"));
  • 87. Entry Point Import Context Render import IssueMacroContainer from '../containers/IssueMacroContainer';
 
 let spaceKey = queryString.parse(location.search)["spaceKey"];
 
 ReactDOM.render(<IssueMacroContainer
 loadIssues={Issues.loadIssues(spaceKey)}/>, document.getElementById("list-issues"));
  • 88. import IssueMacroContainer from '../containers/IssueMacroContainer';
 
 let spaceKey = queryString.parse(location.search)["spaceKey"];
 
 ReactDOM.render(<IssueMacroContainer
 loadIssues={Issues.loadIssues(spaceKey)}/>, document.getElementById("list-issues")); Entry Point Import Context Render
  • 89. import IssueMacroContainer from '../containers/IssueMacroContainer';
 
 let spaceKey = queryString.parse(location.search)["spaceKey"];
 
 ReactDOM.render(<IssueMacroContainer
 loadIssues={Issues.loadIssues(spaceKey)}/>, document.getElementById("list-issues")); Entry Point Import Context Render An entry point will render the top component.
  • 90. webpack takes modules with dependencies and generates static assets representing those modules. http://webpack.github.io/
  • 92. Webpack Configuration Config Entry Output module.exports = {
 resolve: {root: [__dirname + path.sep + 'assets']},
 devtool: 'source-map',
 entry:{
 app: './src/entry-points/App.js',
 dialog: './src/entry-points/NewIssueDialog.js',
 macro: './src/entry-points/Macro.js',
 },
 output: {
 path: "./public/js",
 filename: '[name].js'
 },
 plugins: [ … ],
 module: {
 loaders: [ … ]
 }
 };
  • 93. module.exports = {
 resolve: {root: [__dirname + path.sep + 'assets']},
 devtool: 'source-map',
 entry:{
 app: './src/entry-points/App.js',
 dialog: './src/entry-points/NewIssueDialog.js',
 macro: './src/entry-points/Macro.js',
 },
 output: {
 path: "./public/js",
 filename: '[name].js'
 },
 plugins: [ … ],
 module: {
 loaders: [ … ]
 }
 }; Webpack Configuration Config Entry Output
  • 94. module.exports = {
 resolve: {root: [__dirname + path.sep + 'assets']},
 devtool: 'source-map',
 entry:{
 app: './src/entry-points/App.js',
 dialog: './src/entry-points/NewIssueDialog.js',
 macro: './src/entry-points/Macro.js',
 },
 output: {
 path: "./public/js",
 filename: '[name].js'
 },
 plugins: [ … ],
 module: {
 loaders: [ … ]
 }
 }; Webpack Configuration Config Entry Output
  • 95. module.exports = {
 resolve: {root: [__dirname + path.sep + 'assets']},
 devtool: 'source-map',
 entry:{
 app: './src/entry-points/App.js',
 dialog: './src/entry-points/NewIssueDialog.js',
 macro: './src/entry-points/Macro.js',
 },
 output: {
 path: "./public/js",
 filename: '[name].js'
 },
 plugins: [ … ],
 module: {
 loaders: [ … ]
 }
 }; Webpack Configuration Config Entry Output Webpack will bundle your components into static files that a browser will understand.
  • 97. {{!< layout}}
 <div id="list-issues"/>
 <script src="/js/macro.js"></script> Recap Component State Entry HTML
  • 98. Building Using NPM to build your App
  • 99. Building Package Bundle Start {
 "name": "confluence-issues-addon",
 "version": "0.0.1",
 "private": true,
 "scripts": {
 "start": "node app.js",
 "bundle": "webpack",
 "watch": "webpack --watch",
 "prestart": "webpack"
 },
 "dependencies": { … },
 "devDependencies": {
 "babel-core": "^6.9.0",
 "babel-loader": "^6.2.4",
 "babel-preset-es2015": "^6.9.0",
 "babel-preset-react": "^6.5.0",
 "webpack": "^1.13.0"
 }
 }
  • 100. {
 "name": "confluence-issues-addon",
 "version": "0.0.1",
 "private": true,
 "scripts": {
 "start": "node app.js",
 "bundle": "webpack",
 "watch": "webpack --watch",
 "prestart": "webpack"
 },
 "dependencies": { … },
 "devDependencies": {
 "babel-core": "^6.9.0",
 "babel-loader": "^6.2.4",
 "babel-preset-es2015": "^6.9.0",
 "babel-preset-react": "^6.5.0",
 "webpack": "^1.13.0"
 }
 } Building Package Bundle Start
  • 101. {
 "name": "confluence-issues-addon",
 "version": "0.0.1",
 "private": true,
 "scripts": {
 "start": "node app.js",
 "bundle": "webpack",
 "watch": "webpack --watch",
 "prestart": "webpack"
 },
 "dependencies": { … },
 "devDependencies": {
 "babel-core": "^6.9.0",
 "babel-loader": "^6.2.4",
 "babel-preset-es2015": "^6.9.0",
 "babel-preset-react": "^6.5.0",
 "webpack": "^1.13.0"
 }
 } Building Package Bundle Start
  • 102. {
 "name": "confluence-issues-addon",
 "version": "0.0.1",
 "private": true,
 "scripts": {
 "start": "node app.js",
 "bundle": "webpack",
 "watch": "webpack --watch",
 "prestart": "webpack"
 },
 "dependencies": { … },
 "devDependencies": {
 "babel-core": "^6.9.0",
 "babel-loader": "^6.2.4",
 "babel-preset-es2015": "^6.9.0",
 "babel-preset-react": "^6.5.0",
 "webpack": "^1.13.0"
 }
 } Building Package Bundle Start Babel is used to transpile the extended JavaScript.
  • 104. ES2015 Extensions, modules, dependencies, etc Standalone files, compatible with browsers, etc Transpiling
  • 105. Run a the webpack script Use npm run bundle to run the webpack script we defined in the package.json file. Building Package Bundle Start
  • 106. Building Package Bundle Start Run a the webpack script Use npm run bundle to run the webpack script we defined in the package.json file.
  • 107. Create the static JavaScript files These files are self contained and browser compatible JavaScript resources. Building Package Bundle Start
  • 108. Client app.js Refers to the application entry point component Server app.js Default name for the Node.js server entry point. Two Apps
  • 109. Create the project Use npm start to start the node app, automatically transpiling the client components. Building Package Bundle Start
  • 110. Building Package Bundle Start Create the project Use npm start to start the node app, automatically transpiling the client components.
  • 111. Building Package Bundle Start Create the project Use npm start to start the node app, automatically transpiling the client components.
  • 112. Building Package Bundle Start Create the project Use npm start to start the node app, automatically transpiling the client components.
  • 113.
  • 114. Node JS Uses NPM to package both server and client side modules. Manually Package You can still use NPM to bundle your client side components Dev Loop Restart your add-on service and call webpack on each change.
  • 115. Recap Quick recap of what we have learnt so far
  • 116. Recap Component State Entry class Clock extends React.Component {
 constructor(props) {
 super(props);
 this.state = {date: moment()};
 }
 
 componentDidMount() {
 this.timerID = setInterval(() => this.tick(), 1000);
 }
 
 componentWillUnmount() {
 clearInterval(this.timerID);
 }
 
 tick() {
 this.setState({date: moment()});
 }
 
 render() {
 return (<span>{this.state.date.format(this.props.dateFormat)}</span>);
 }
 }
 
 Clock.defaultProps = {
 'dateFormat': 'MMMM Do YYYY, h:mm:ss'
 };
 
 export default Clock; Building
  • 117. class Clock extends React.Component {
 constructor(props) {
 super(props);
 this.state = {date: moment()};
 }
 
 componentDidMount() {
 this.timerID = setInterval(() => this.tick(), 1000);
 }
 
 componentWillUnmount() {
 clearInterval(this.timerID);
 }
 
 tick() {
 this.setState({date: moment()});
 }
 
 render() {
 return (<span>{this.state.date.format(this.props.dateFormat)}</span>);
 }
 }
 
 Clock.defaultProps = {
 'dateFormat': 'MMMM Do YYYY, h:mm:ss'
 };
 
 export default Clock; Recap Component State Entry Building
  • 118. class Clock extends React.Component {
 constructor(props) {
 super(props);
 this.state = {date: moment()};
 }
 
 componentDidMount() {
 this.timerID = setInterval(() => this.tick(), 1000);
 }
 
 componentWillUnmount() {
 clearInterval(this.timerID);
 }
 
 tick() {
 this.setState({date: moment()});
 }
 
 render() {
 return (<span>{this.state.date.format(this.props.dateFormat)}</span>);
 }
 }
 
 Clock.defaultProps = {
 'dateFormat': 'MMMM Do YYYY, h:mm:ss'
 };
 
 export default Clock; Recap Component State Entry Building
  • 119. class Clock extends React.Component {
 constructor(props) {
 super(props);
 this.state = {date: moment()};
 }
 
 componentDidMount() {
 this.timerID = setInterval(() => this.tick(), 1000);
 }
 
 componentWillUnmount() {
 clearInterval(this.timerID);
 }
 
 tick() {
 this.setState({date: moment()});
 }
 
 render() {
 return (<span>{this.state.date.format(this.props.dateFormat)}</span>);
 }
 }
 
 Clock.defaultProps = {
 'dateFormat': 'MMMM Do YYYY, h:mm:ss'
 };
 
 export default Clock; Recap Component State Entry Building
  • 120. ReactDOM.render(
 <Clock dateFormat='MMMM Do YYYY, h:mm:ss'/>,
 document.getElementById('clock')
 ); Recap Component State Entry Building
  • 121. module.exports = {
 resolve: { root: [__dirname + path.sep + 'assets'] },
 devtool: 'source-map',
 entry:{
 router: './assets/js/Clock'
 },
 output: {
 path: "./public/js",
 filename: '[name].js'
 },
 plugins: [ … ],
 module: {
 loaders: [ …]
 }
 }; Recap Component State Entry Building
  • 122. module.exports = {
 resolve: { root: [__dirname + path.sep + 'assets'] },
 devtool: 'source-map',
 entry:{
 router: './assets/js/Clock'
 },
 output: {
 path: "./public/js",
 filename: '[name].js'
 },
 plugins: [ … ],
 module: {
 loaders: [ …]
 }
 }; Recap Component State Entry Building
  • 123. {{!< layout}}
 <div id="clock"/>
 <script src="/js/clock.js"></script> Recap Component State Entry Building
  • 124. Recap Component State Entry Building NPM Use npm to manage webpack dependencies and development lifecycle.
  • 125. Onward Related Concepts and Advanced Topics
  • 126. Onward AtlasKit Other Extensions React Components AtlasKit components are React Components. NPM Modules Define a dependency on the AtlasKit component and import it in to your own! AtlasKit
  • 131. Thank you! MATTHEW JENSEN | TEAM LEAD | ATLASSIAN | @MATTHEWJENSEN